@com55/node-red-ai-access 0.2.0-beta.7 → 0.2.0-beta.8

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/plugin.cjs CHANGED
@@ -1,15 +1,15 @@
1
- "use strict";var Vc=Object.create;var qn=Object.defineProperty;var Yc=Object.getOwnPropertyDescriptor;var Xc=Object.getOwnPropertyNames;var Zc=Object.getPrototypeOf,Qc=Object.prototype.hasOwnProperty;var a=(e,t)=>qn(e,"name",{value:t,configurable:!0});var q=(e,t)=>()=>(e&&(t=e(e=0)),t);var Ki=(e,t)=>{for(var n in t)qn(e,n,{get:t[n],enumerable:!0})},Ji=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of Xc(t))!Qc.call(e,o)&&o!==n&&qn(e,o,{get:()=>t[o],enumerable:!(r=Yc(t,o))||r.enumerable});return e};var zn=(e,t,n)=>(n=e!=null?Vc(Zc(e)):{},Ji(t||!e||!e.__esModule?qn(n,"default",{value:e,enumerable:!0}):n,e)),eu=e=>Ji(qn({},"__esModule",{value:!0}),e);function ft(e,t,n){let r=n.pretty?`${JSON.stringify(e,null,2)}
1
+ "use strict";var tu=Object.create;var Jn=Object.defineProperty;var nu=Object.getOwnPropertyDescriptor;var ru=Object.getOwnPropertyNames;var ou=Object.getPrototypeOf,su=Object.prototype.hasOwnProperty;var a=(e,t)=>Jn(e,"name",{value:t,configurable:!0});var q=(e,t)=>()=>(e&&(t=e(e=0)),t);var Qi=(e,t)=>{for(var n in t)Jn(e,n,{get:t[n],enumerable:!0})},ea=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of ru(t))!su.call(e,o)&&o!==n&&Jn(e,o,{get:()=>t[o],enumerable:!(r=nu(t,o))||r.enumerable});return e};var Vn=(e,t,n)=>(n=e!=null?tu(ou(e)):{},ea(t||!e||!e.__esModule?Jn(n,"default",{value:e,enumerable:!0}):n,e)),iu=e=>ea(Jn({},"__esModule",{value:!0}),e);function ht(e,t,n){let r=n.pretty?`${JSON.stringify(e,null,2)}
2
2
  `:`${JSON.stringify(e)}
3
- `,o=Buffer.from(r,"utf8");if(o.byteLength>t)throw new le(n.kind,o.byteLength,t);return{bytes:o}}function Yi(e,t){let n=(0,Wn.resolve)(t),r=(0,Wn.resolve)(e);if(r!==n&&!r.startsWith(n+Wn.sep))throw new Error("Path escapes .ai-access storage root");return r}function cn(e,t,n){let r=Yi(t,e),o=`${r}.${process.pid}.tmp`;(0,st.writeFileSync)(o,n.bytes,{mode:Or}),(0,st.renameSync)(o,r),(0,st.chmodSync)(r,Or)}function Xi(e,t,n){let r=Yi(t,e);(0,st.appendFileSync)(r,n.bytes,{mode:Or});try{(0,st.chmodSync)(r,Or)}catch{}}var st,Wn,hs,Or,K,Vi,le,se=q(()=>{"use strict";st=require("fs"),Wn=require("path"),hs=448,Or=384,K=15e5,Vi=8192,le=class extends Error{constructor(n,r,o){super(`${n} size ${r} bytes exceeds limit of ${o} bytes`);this.kind=n;this.size=r;this.maxBytes=o;this.name="StoragePayloadTooLargeError"}static{a(this,"StoragePayloadTooLargeError")}code="payload_too_large";statusCode=413};a(ft,"encodeTrustedJson");a(Yi,"assertUnderAiAccess");a(cn,"writeTrustedAtomic");a(Xi,"appendTrusted")});function Kn(e){if(typeof e!="string"||e.length===0||e.length>2048)return;let t;try{t=new URL(e)}catch{return}if(!(t.protocol!=="http:"&&t.protocol!=="https:")&&!(t.username||t.password||t.search||t.hash)&&!(t.pathname!=="/"||/^[a-z][a-z0-9+.-]*:\/\/[^/]*\//i.test(e)))return t.origin.toLowerCase()}function su(e,t){let n=e.getHeader("Vary"),r=String(n??"").split(",").map(o=>o.trim()).filter(Boolean);r.some(o=>o.toLowerCase()===t.toLowerCase())||r.push(t),e.setHeader("Vary",r.join(", "))}function Qi(e){return(t,n,r)=>{if(!tu.test(t.path)){r();return}for(let c of n.getHeaderNames())c.startsWith("access-control-")&&n.removeHeader(c);let o=n.setHeader.bind(n),s=!1;n.setHeader=((c,u)=>!s&&/^access-control-/i.test(c)?n:o(c,u));let i=Kn(t.get("origin")),d=i===void 0?void 0:e().find(c=>c===i),l=d!==void 0;if(su(n,"Origin"),!l){if(t.method==="OPTIONS"&&t.get("access-control-request-method")){n.status(204).end();return}r();return}if(s=!0,n.setHeader("Access-Control-Allow-Origin",d),n.setHeader("Access-Control-Expose-Headers",ou),t.method==="OPTIONS"&&t.get("access-control-request-method")){n.setHeader("Access-Control-Allow-Methods",nu),n.setHeader("Access-Control-Allow-Headers",ru),n.setHeader("Access-Control-Max-Age",String(600)),n.status(204).end();return}s=!1,r()}}function ea(e,t){let n=e.parent,r=n&&typeof n.use=="function"?n:e;r.use(t);let o=r,i=(o._router??o.router)?.stack;if(!Array.isArray(i)||i.length<2)return;let d=i.pop(),l=0;for(;l<i.length;){let c=i[l].name;if(c!=="query"&&c!=="expressInit")break;l+=1}i.splice(l,0,d)}var tu,nu,ru,ou,Dr=q(()=>{"use strict";tu=/^\/mcp(?:\/?$|\/(?:a(?:\/|$)|api(?:\/|$)|rpc\/?$))/,nu="GET, POST, DELETE, OPTIONS",ru="Authorization, Content-Type, Accept, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID",ou="Mcp-Session-Id, WWW-Authenticate, Retry-After, RateLimit, RateLimit-Policy, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset";a(Kn,"canonicalOrigin");a(su,"appendVary");a(Qi,"aiCorsMiddleware");a(ea,"installAiCorsMiddleware")});function jr(e){return e.replace(/([a-z])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/([A-Za-z])([0-9])/g,"$1 $2").replace(/([0-9])([A-Za-z])/g,"$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean).map(t=>t.toUpperCase())}function Vn(e){if(!Array.isArray(e))return;let t=[];for(let n of e){if(typeof n!="string")continue;let r=n.trim().toUpperCase();if(!(!Hr.test(r)||t.includes(r))&&(t.push(r),t.length>=Jn))break}return t}var J,Ft,Hr,Jn,un=q(()=>{"use strict";J="[REDACTED]",Ft=Object.freeze(["PASSWORD","PASSWD","PASS","PWD","PW","PASSPHRASE","SECRET","CLIENT_SECRET","TOKEN","API_KEY","APIKEY","KEY","PRIVATE","CREDENTIAL","AUTH","AUTHORIZATION","BEARER","SESSION","COOKIE","CONNECTION_STRING","CONN_STR","CONN","DSN"]),Hr=/^[A-Za-z0-9_-]{2,40}$/,Jn=100;a(jr,"tokenizeName");a(Vn,"normalizeSecretNamePatterns")});function au(e){return e==="nodes"||e==="flows"||e==="full"}function Ur(e,t){if(!e||typeof e!="object"||Array.isArray(e))return{};let n=e,r={};if(typeof n.enabled=="boolean"&&(r.enabled=n.enabled),typeof n.allowWithoutAdminAuth=="boolean"&&(r.allowWithoutAdminAuth=n.allowWithoutAdminAuth),typeof n.defaultLinkTtlSeconds=="number"&&Number.isFinite(n.defaultLinkTtlSeconds)&&n.defaultLinkTtlSeconds>0&&(r.defaultLinkTtlSeconds=Math.floor(n.defaultLinkTtlSeconds)),typeof n.proposalTtlDays=="number"&&Number.isInteger(n.proposalTtlDays)&&n.proposalTtlDays>=1&&n.proposalTtlDays<=365&&(r.proposalTtlDays=n.proposalTtlDays),au(n.defaultDeploymentType)&&(r.defaultDeploymentType=n.defaultDeploymentType),typeof n.maxActiveLinks=="number"&&Number.isFinite(n.maxActiveLinks)&&n.maxActiveLinks>0&&(r.maxActiveLinks=Math.floor(n.maxActiveLinks)),typeof n.publicBaseUrl=="string")try{r.publicBaseUrl=hn(n.publicBaseUrl)}catch{t?.(`AI Access: invalid publicBaseUrl value ${JSON.stringify(n.publicBaseUrl)} was dropped`)}if(Array.isArray(n.corsAllowedOrigins)){let o=new Set;for(let s of n.corsAllowedOrigins){let i=Kn(s);i?o.add(i):t?.(`AI Access: invalid CORS origin value ${JSON.stringify(s)} was dropped`)}r.corsAllowedOrigins=[...o]}for(let o of["rateLimitWindowMs","rateLimitMaxPerIp","rateLimitMaxPerToken"]){let s=n[o],i=o==="rateLimitWindowMs"?100:1,d=o==="rateLimitWindowMs"?36e5:1e6;typeof s=="number"&&Number.isInteger(s)&&s>=i&&s<=d&&(r[o]=s)}if(n.secretNamePatterns!==void 0){let o=Vn(n.secretNamePatterns);o?(Array.isArray(n.secretNamePatterns)&&o.length<n.secretNamePatterns.length&&t?.("AI Access: invalid or duplicate secretNamePatterns entries were dropped"),r.secretNamePatterns=o):t?.("AI Access: secretNamePatterns must be a list of strings; ignored")}if(n.oauth&&typeof n.oauth=="object"&&!Array.isArray(n.oauth)){let o=n.oauth;r.oauth={},typeof o.enabled=="boolean"&&(r.oauth.enabled=o.enabled);for(let i of["accessTokenTtlSec","refreshIdleTtlSec","dcrMaxClients"]){let d=o[i];typeof d=="number"&&Number.isInteger(d)&&d>0&&d<=31536e3&&(r.oauth[i]=d)}let s=o.refreshMaxLifetimeSec;typeof s=="number"&&Number.isInteger(s)&&s>=0&&s<=31536e4&&(r.oauth.refreshMaxLifetimeSec=s)}return r}function du(e,t){if(!(0,Xn.existsSync)(e))return{};try{let n=(0,Xn.readFileSync)(e,"utf8");return Ur(JSON.parse(n),t)}catch(n){return t?.(`AI Access: config.json is corrupt or unreadable; defaults are used (${String(n)})`),{}}}function Br(e,t){let n={},r={...Yn};for(let s of Object.keys(Yn).filter(i=>i!=="oauth"))n[s]=e[s]!==void 0?"settings.js":t[s]!==void 0?"disk":"default",Object.assign(r,{[s]:e[s]??t[s]??Yn[s]});let o={...r,sources:n};return e.oauth!==void 0||t.oauth!==void 0?(o.oauth={...pn,...t.oauth??{},...e.oauth??{}},n.oauth=e.oauth!==void 0?"settings.js":"disk"):(o.oauth={...pn},n.oauth="default"),o}function ys(e,t){let n=Ur(e??{},t);for(let o of Object.keys(Yn).filter(s=>s!=="oauth"))e&&Object.hasOwn(e,o)&&n[o]===void 0&&Object.assign(n,{[o]:Yn[o]});let r=e?.oauth;if(r&&typeof r=="object"&&!Array.isArray(r)){let o=n.oauth??={};for(let s of Object.keys(pn))Object.hasOwn(r,s)&&o[s]===void 0&&(t?.(`AI Access: settings.js aiAccess.oauth.${s} is invalid; using the default (${String(pn[s])})`),Object.assign(o,{[s]:pn[s]}))}return n}var Xn,gs,iu,pn,Yn,fn,qr=q(()=>{"use strict";Xn=require("fs"),gs=require("path");ve();Dr();un();se();iu=16384,pn={enabled:!1,accessTokenTtlSec:3600,refreshIdleTtlSec:2592e3,refreshMaxLifetimeSec:7776e3,dcrMaxClients:5},Yn={enabled:!0,allowWithoutAdminAuth:!1,defaultLinkTtlSeconds:14400,proposalTtlDays:7,maxActiveLinks:10,defaultDeploymentType:"nodes",publicBaseUrl:"",corsAllowedOrigins:[],rateLimitWindowMs:6e4,rateLimitMaxPerIp:600,rateLimitMaxPerToken:300,secretNamePatterns:[...Ft],oauth:{...pn}};a(au,"isDeploymentType");a(Ur,"sanitizePartial");a(du,"readDiskFile");a(Br,"mergeAiAccessSettings");a(ys,"settingsJsPartial");fn=class{constructor(t,n,r){this.paths=t;this.filePath=(0,gs.join)(t.root,"config.json"),this.settingsJs=ys(n,r),this.disk=du((0,Xn.existsSync)(this.filePath)?this.filePath:(0,gs.join)(t.root,"settings.json"),r),this.cached=Br(this.settingsJs,this.disk)}static{a(this,"SettingsStore")}filePath;disk;settingsJs;cached;getEffective(){return this.cached}getDisk(){return{...this.disk}}update(t){let n=Ur(t);for(let r of Object.keys(this.settingsJs))delete n[r];return this.persist(n),this.disk=n,this.cached=Br(this.settingsJs,this.disk),this.cached}patch(t,n=[]){let r=Ur(t),o={...this.disk,...r,...r.oauth?{oauth:{...this.disk.oauth,...r.oauth}}:{}};for(let s of n)delete o[s];return this.update(o)}persist(t){let n=ft(t,iu,{kind:"settings",pretty:!0});cn(this.paths.root,this.filePath,n)}}});function Wr(e){zr=e}function ta(){return zr}function oe(e){return e.adminAuth!=null&&e.adminAuth!==!1}function Ee(e){return zr?zr.getEffective():Br(ys(e.aiAccess),{})}function Ce(e){let t=Ee(e);return t.enabled?oe(e)?!0:t.allowWithoutAdminAuth:!1}var zr,ke=q(()=>{"use strict";qr();zr=null;a(Wr,"bindSettingsStore");a(ta,"getSettingsStore");a(oe,"isAdminAuthEnabled");a(Ee,"resolveAiAccessSettings");a(Ce,"areAiFeaturesEnabled")});function Ae(e){let t=`/${e??""}`.replace(/\/{2,}/g,"/");for(;t.length>1&&t.endsWith("/");)t=t.slice(0,-1);return t}function Se(e){let t=Ae(e);return t==="/"?"/mcp/api/v1":`${t}/mcp/api/v1`}function Zn(e){let t=Ae(e);return t==="/"?"/ai-access":`${t}/ai-access`}function ht(e){let t=Ae(e);return t==="/"?"/mcp/rpc":`${t}/mcp/rpc`}function lu(e){let t=e.authInfo?.scope;if(t)return Array.isArray(t)?t:[t];let n=e.user?.permissions;return n?Array.isArray(n)?n:[n]:[]}function mt(e){if(e.auth)return e.auth.scopes.includes("propose")?"propose":e.auth.scopes.includes("read")?"read":null;let t=lu(e);return t.includes("*")?"*":t.includes("read")?"read":null}function cu(e,t){return!t||!e?{view:!0,manageLinks:!1,approveProposals:!1}:e==="*"?{view:!0,manageLinks:!0,approveProposals:!0}:{view:!0,manageLinks:!1,approveProposals:!1}}function na(e,t={error:"Authentication required",code:"auth_required"},n){let r=e.end.bind(e),o=!0;return e.end=((...s)=>o&&e.statusCode===401&&!e.headersSent?(o=!1,e.removeHeader("Content-Length"),e.setHeader("Content-Type","application/json; charset=utf-8"),n&&e.setHeader("WWW-Authenticate",n),r(JSON.stringify(t))):r(...s)),()=>{o=!1,e.end=r}}function Ie(e,t){let n="read";return(r,o,s)=>{if(!oe(e.settings)){s();return}let i=t==="api"?na(o):null;e.auth.needsPermission(n)(r,o,d=>{if(i?.(),d){s(d);return}if(!r.user){if(t==="page"){o.redirect(Ae(e.settings.httpAdminRoot));return}o.status(401).json({error:"Authentication required",code:"auth_required"});return}if(!mt(r)){o.status(401).json({error:"Insufficient permissions",code:"insufficient_permissions"});return}s()})}}function Gr(e,t){let n=oe(e.settings),r=Ee(e.settings),o=Ce(e.settings),s=null;return n?s=mt(t):o&&(s="*"),{adminAuthEnabled:n,aiFeaturesEnabled:o,allowWithoutAdminAuth:r.allowWithoutAdminAuth,permission:s,capabilities:cu(s,o)}}function mn(e){return(t,n,r)=>{if(!Ce(e.settings)){n.status(403).json({error:"AI Access features are disabled. Enable adminAuth, or set aiAccess.allowWithoutAdminAuth to true for local-only use.",code:"ai_features_disabled"});return}r()}}function Qn(e){return(t,n,r)=>{if(!oe(e.settings)){if(Ce(e.settings)){r();return}n.status(403).json({error:"AI Access features are disabled",code:"ai_features_disabled"});return}if(mt(t)!=="*"){n.status(403).json({error:"Full admin permission (*) is required for this action",code:"insufficient_permissions"});return}r()}}function uu(e){return e.startsWith("aia_")}function pu(e){return uu(e)?!0:e.length<24}function fu(e){if(e.length<24||e.startsWith("aia_")&&e.length<40)return"The token may be truncated \u2014 ask the user for the full access-link token."}function ra(e,t,n,r){if(!oe(e.settings)){r();return}let o={error:xe,code:qe},s=na(n,o,ce);e.auth.needsPermission("read")(t,n,i=>{if(s(),i){r(i);return}if(!n.headersSent){if(!t.user||!mt(t)){n.setHeader("WWW-Authenticate",ce),n.status(401).json(o);return}r()}})}function oa(e,t,n,r,o){let s=e.headers.authorization;if(typeof s!="string"||!/^Bearer\s+\S+/i.test(s.trim()))return"continue";let d=/^Bearer\s+(\S+)$/i.exec(s.trim())?.[1];if(!d)return"continue";let l=n(d);if(l.status==="active")return l.link.kind!==r?(nr(t,r,l.link.kind),"wrong_kind"):(e.accessLink=l.link,"ok");if(l.status==="expired")return tr(e,t,o,l.expiresAt),"invalid";if(pu(d)){t.setHeader("WWW-Authenticate",ce);let c=fu(d);return t.status(401).json({error:Ot,code:er,...c?{hint:c}:{}}),"invalid"}return"continue"}function sa(e,t,n="rest"){return(r,o,s)=>{if(!Ce(e.settings)){o.status(403).json({error:"AI Access features are disabled. Enable adminAuth, or set aiAccess.allowWithoutAdminAuth to true for local-only use.",code:"ai_features_disabled"});return}let i=oa(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||ra(e,r,o,s)}}function ia(e,t,n="rest"){return(r,o,s)=>{let i=oa(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||ra(e,r,o,s)}}var Be=q(()=>{"use strict";$t();ke();a(Ae,"normalizeHttpAdminRoot");a(Se,"mcpApiBase");a(Zn,"dashboardPath");a(ht,"mcpRpcPath");a(lu,"scopesFromRequest");a(mt,"resolvePermission");a(cu,"capabilitiesFor");a(na,"interceptNeedsPermissionUnauthorized");a(Ie,"requireAdminSession");a(Gr,"describeAccess");a(mn,"requireAiFeatures");a(Qn,"requireStarPermission");a(uu,"looksLikeAccessLinkToken");a(pu,"looksLikeFailedAccessLinkAttempt");a(fu,"truncatedTokenHint");a(ra,"continueAsAdminSessionOrAccessLinkMissing");a(oa,"tryAccessLinkBearer");a(sa,"requireAdminReadOrAccessLink");a(ia,"requireAdminSessionOrAccessLink")});function hu(e,t){let n=Ae(e);return`${n==="/"?"/mcp/a":`${n}/mcp/a`}/${t}`}function mu(e){return!!e.app.get("trust proxy")}function aa(e,t){let n=e.get(t);if(typeof n!="string"||n.length===0)return;let r=n.split(",")[0]?.trim();return r&&r.length>0?r:void 0}function gu(e){if(typeof e!="string"||e.trim().length===0)return;let t;try{t=new URL(e.trim())}catch{return}if(!(t.protocol!=="http:"&&t.protocol!=="https:"))return`${t.origin}${t.pathname.replace(/\/+$/,"")}`}function Mt(e){try{let n=new URL(`http://${e.includes(":")&&!e.startsWith("[")?`[${e}]`:e}`).hostname.replace(/^\[|\]$/g,"");if(!(0,ws.isIP)(n))return;if(n.startsWith("::ffff:")){let r=n.slice(7);if((0,ws.isIP)(r)===4)return r;let o=r.split(":");if(o.length===2){let s=parseInt(o[0],16)*65536+parseInt(o[1],16);return[24,16,8,0].map(i=>s>>>i&255).join(".")}}return n}catch{return}}function hn(e){if(e==="")return"";let t="publicBaseUrl must be an HTTP(S) origin with no path, query, fragment, or credentials";if(typeof e!="string")throw new Error(t);let n;try{n=new URL(e.trim())}catch{throw new Error(t)}if(n.protocol!=="https:"&&n.protocol!=="http:"||!/^https?:\/\/[^/?#]+\/?$/i.test(e.trim())||n.pathname!=="/"||n.search||n.hash||n.username||n.password||e.includes("?")||e.includes("#"))throw new Error(t);return n.origin}function Kr(e){bs=gu(e)}function la(e){if(typeof e!="string"||e.length===0||e.length>260)return!1;let t,n,r=/^(\[[^\]]*\])(?::(\d+))?$/.exec(e);if(r){if(t=r[1],n=r[2],!bu.test(t))return!1;try{new URL(`http://${t}/`)}catch{return!1}}else{let o=/^([^:]+)(?::(\d+))?$/.exec(e);if(!o||(t=o[1],n=o[2],t.length>253)||!wu.test(t)&&!yu.test(t))return!1}if(n!==void 0){if(!/^\d{1,5}$/.test(n))return!1;let o=Number(n);if(o<1||o>65535)return!1}return!0}function ua(e){let t=e?.trim().toLowerCase();return t==="http"||t==="https"?t:void 0}function vu(e){let t=e.socket;return t?.encrypted?"https":t?"http":ua(e.protocol)??"http"}function vs(e){if(bs)return bs;let t=mu(e),n=t?aa(e,"x-forwarded-host"):void 0,r=e.get("host")?.trim(),o=la(n)?n:la(r)?r:void 0;if(!o)return;let s=(t?ua(aa(e,"x-forwarded-proto")):void 0)??vu(e);try{return new URL(`${s}://${o}`).origin}catch{return}}function rr(e,t){let n=vs(e);return n?`${n}${t}`:t}function pa(e,t,n){return rr(e,hu(t,n))}function ks(e){return e.replace(/\/mcp\/a\/[^/?#]+/gi,"/mcp/a/<redacted>")}var ws,gn,ca,bs,da,yu,wu,bu,ve=q(()=>{"use strict";ws=require("net");Be();gn=8192,ca=1800*1e3;a(hu,"mcpAccessLinkPath");a(mu,"isTrustProxyEnabled");a(aa,"firstForwardedValue");a(gu,"normalizePublicBaseUrl");a(Mt,"canonicalizeIp");a(hn,"validateSettingsPublicBaseUrl");a(Kr,"bindPublicBaseUrl");da="[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?",yu=new RegExp(`^${da}(?:\\.${da})*$`),wu=/^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/,bu=/^\[[0-9A-Fa-f:.]+\]$/;a(la,"isValidHostHeader");a(ua,"validScheme");a(vu,"connectionScheme");a(vs,"requestPublicOrigin");a(rr,"absolutizePath");a(pa,"buildAccessLinkUrl");a(ks,"redactAccessLinkTokenInPath")});function Ru(e){if(!e||e.trim().length===0)return{kind:"missing"};let t=e.trim();if(/^Basic\s+/i.test(t))return{kind:"basic"};let n=/^Bearer\s+(\S+)$/i.exec(t);return n?.[1]?{kind:"bearer",token:n[1]}:{kind:"other"}}function As(e,t,n,r){e.setHeader("WWW-Authenticate",ce),e.status(401).json({error:t,code:n,...r?{hint:r}:{}})}function Pu(e){let t=e??"";for(;t.endsWith("/");)t=t.slice(0,-1);return`${t}/ai-access#links`}function _u(e,t,n,r){let o=rr(e,Pu(t));return{error:ku,code:Su,...n?{expiredAt:n}:{},accessLinksUrl:o,hint:r??`Ask the user to create a new access link in Node-RED on the /ai-access dashboard \u2192 Access tokens (${o}).`}}function tr(e,t,n,r,o){t.setHeader("WWW-Authenticate",ce),t.status(401).json(_u(e,n,r,o))}function Eu(e){if(e.length<24||e.startsWith("aia_")&&e.length<40)return"The token may be truncated \u2014 ask the user for the full access-link token."}function Jr(e){return e==="get_link"?"Please open Node-RED \u2192 /ai-access \u2192 Access tokens and create a get_link access URL, then send me that /mcp/a/<token> link.":`Please open Node-RED \u2192 /ai-access \u2192 Access tokens and create a kind=${e} token, then send me the Bearer token (not a get_link path URL).`}function Iu(e,t){let n=Cu;return e==="get_link"?`This URL requires a get_link token. This token is kind=${t} \u2014 ${n} for a get_link access URL (/mcp/a/<token>/\u2026). What to tell the user: ${Jr("get_link")}`:e==="rest"?`This REST endpoint requires a rest token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${Jr("rest")}`:`This MCP endpoint requires an mcp token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${Jr("mcp")}`}function nr(e,t,n){e.setHeader("WWW-Authenticate",ce),e.status(403).json({error:`Access link kind mismatch: expected ${t}, got ${n}`,code:Tu,expectedKind:t,actualKind:n,hint:Iu(t,n),tellUser:Jr(t)})}function Dt(e,t,n){return(r,o,s)=>{if(!Ce(e.settings)){o.status(403).json({error:"AI Access features are disabled. Enable adminAuth, or set aiAccess.allowWithoutAdminAuth to true for local-only use.",code:"ai_features_disabled"});return}let i=Ru(r.headers.authorization);if(i.kind==="basic"){As(o,Au,xu);return}if(i.kind==="missing"||i.kind==="other"){As(o,xe,qe);return}let d=t.validateResult(i.token);if(d.status==="expired"){tr(r,o,e.settings.httpAdminRoot,d.expiresAt);return}if(d.status!=="active"){As(o,Ot,er,Eu(i.token));return}if(d.link.kind!==n){nr(o,n,d.link.kind);return}r.accessLink=d.link,s()}}var Ot,ku,xe,Au,ce,qe,er,Su,xu,Tu,Cu,$t=q(()=>{"use strict";ve();ke();Ot="Access link is invalid, revoked, or no longer available (it may have expired, or Node-RED may have restarted). Ask the user for a new access link.",ku="Access link token has expired. Ask the user for a new access link.",xe="Access link Bearer token is missing. Ask the user for a new access link and send Authorization: Bearer <token>.",Au="Authorization must use the Bearer scheme (Authorization: Bearer <access-link-token>), not Basic. Ask the user for a new access link.",ce='Bearer realm="node-red-ai-access", charset="UTF-8"',qe="access_link_missing",er="access_link_invalid",Su="token_expired",xu="bearer_scheme_required",Tu="wrong_token_kind";a(Ru,"parseAuthorizationHeader");a(As,"sendUnauthorized");a(Pu,"accessLinksPagePath");a(_u,"tokenExpiredBody");a(tr,"sendTokenExpired");a(Eu,"truncatedBearerHint");Cu="ask the owner: Node-RED /ai-access \u2192 Access tokens \u2192 create kind=rest/mcp (or get_link)";a(Jr,"whatToTellUserWrongTokenKind");a(Iu,"wrongTokenKindHint");a(nr,"sendWrongTokenKind");a(Dt,"requireAccessLink")});var te,X,Le,ze,Yr,Te=q(()=>{"use strict";te=class extends Error{static{a(this,"FlowRevisionConflictError")}code="version_mismatch";statusCode=409;expectedRev;currentRev;context;constructor(t,n,r="updateFlow"){super(`Flow revision conflict in ${r}: expectedRev '${t}' does not match current rev '${n}'. The flows changed since you last read them \u2014 re-read flows, re-apply your change, and retry.`),this.name="FlowRevisionConflictError",this.expectedRev=t,this.currentRev=n,this.context=r}},X=class extends Error{static{a(this,"FlowNotFoundError")}code="flow_not_found";statusCode=404;flowId;constructor(t){super(`Flow '${t}' was not found`),this.name="FlowNotFoundError",this.flowId=t}},Le=class extends Error{static{a(this,"AdapterUnavailableError")}code="adapter_unavailable";statusCode=502;constructor(t){super(t),this.name="AdapterUnavailableError"}},ze=class extends Error{static{a(this,"PluginMisconfiguredError")}code="plugin_misconfigured";statusCode=502;constructor(t){super(t),this.name="PluginMisconfiguredError"}},Yr="Node-RED AI Access could not use the in-process flow runtime and the HTTP Admin API fallback returned Unauthorized. The plugin is misconfigured \u2014 tell the admin to ensure the plugin loads inside Node-RED so RED.runtime.flows is available (do not rely on unauthenticated localhost HTTP when adminAuth is enabled)."});function We(e,t){if(!e||!Lu.test(e)||e.includes(".."))throw new Error(`Invalid ${t}`);return e}function Xr(e){if(!e||!Nu.test(e)||e.includes(".."))throw new Error("Invalid proposal id");return e}function Ss(e,t){let n=(0,Ht.basename)(t);if(!n||n!==t||n.includes(".."))throw new Error("Invalid file name");let r=(0,Ht.resolve)(e),o=(0,Ht.resolve)(r,n);if(o!==r&&!o.startsWith(r+Ht.sep))throw new Error("Path escapes storage directory");return o}function fa(e,t=1e5){if(typeof e=="string")return e.replace(/\0/g,"").slice(0,t)}function it(e){let t=e&&typeof e=="object"&&!Array.isArray(e)?e:{},n={};for(let o of Fu){if(!(o in t))continue;let s=t[o];if(o==="disabled"&&typeof s=="boolean"){n[o]=s;continue}if(o==="env"&&Array.isArray(s)){let d=JSON.stringify(s);d.length<=5e5&&(n[o]=JSON.parse(d));continue}let i=fa(s,1e4);i!==void 0&&(n[o]=i)}for(let o of["nodes","configs"]){let s=t[o];if(!Array.isArray(s))continue;let i=JSON.stringify(s),d=Buffer.byteLength(i,"utf8");if(d>K)throw new le(`flow.${o}`,d,K);n[o]=JSON.parse(i)}let r=Buffer.byteLength(JSON.stringify(n),"utf8");if(r>K)throw new le("flow",r,K);return n}function ha(e){let t={};for(let n of Ou){if(!(n in e))continue;let r=e[n];if($u.has(n)){if(typeof r=="number"&&Number.isFinite(r))t[n]=r;else if(typeof r=="string"&&r.trim()!==""){let s=Number(r);Number.isFinite(s)&&(t[n]=s)}continue}let o=fa(r,2e3);o!==void 0&&(t[n]=o)}return t}var Ht,Lu,Nu,Fu,Ou,$u,Zr=q(()=>{"use strict";Ht=require("path");se();Lu=/^[A-Za-z0-9_.-]+$/,Nu=/^prop-[A-Za-z0-9_-]+$/,Fu=["id","type","label","disabled","info","env"],Ou=["proposalId","flowId","expectedRev","resultRev","reason","code","currentRev","title","displayName","flowLabel","linkId","label","expiresAt","createdAt","ttlSeconds","kind","redactions","restoredRedactions","operation"],$u=new Set(["ttlSeconds","redactions","restoredRedactions"]);a(We,"assertSafeSegment");a(Xr,"assertSafeProposalId");a(Ss,"resolveUnderDir");a(fa,"sanitizeString");a(it,"sanitizeFlowTab");a(ha,"sanitizeAuditDetails")});function U(e){return e==="pending"||e==="conflict"}function to(e,t){return`oauth-${(0,eo.createHash)("sha256").update(`${e}\0${t}`).digest("hex")}`}function Ut(e){return e.kind==="oauth"?to(e.clientId,e.user):e.linkId}function Mu(e){return e==="pending"||e==="conflict"||e==="approved"||e==="rejected"||e==="failed"||e==="withdrawn"||e==="expired"||e==="superseded"}function Ge(e,t){return typeof e=="string"&&e.length>0?e.replace(/\0/g,"").slice(0,t):void 0}function Hu(e){if(!e||typeof e!="object"||Array.isArray(e))return;let t=e,n=Ge(t.rev,200),r=t.state;if(!n||r!=="clean"&&r!=="merged"&&r!=="conflict")return;let o=[];if(Array.isArray(t.conflicts))for(let d of t.conflicts.slice(0,200)){if(!d||typeof d!="object")continue;let l=d,c=Ge(l.kind,64),u=Ge(l.message,500);if(!c||!u)continue;let p={kind:c,message:u,choices:Array.isArray(l.choices)?l.choices.filter(g=>g==="live"||g==="proposed"):[]},f=Ge(l.nodeId,200);f&&(p.nodeId=f);let h=Ge(l.nodeType,200);h&&(p.nodeType=h);let m=Ge(l.nodeName,120);m&&(p.nodeName=m),Array.isArray(l.fields)&&(p.fields=l.fields.filter(g=>typeof g=="string"&&g.length>0).slice(0,100).map(g=>g.replace(/\0/g,"").slice(0,200))),o.push(p)}let s={rev:n,state:r,conflicts:o,liveChangedCount:typeof t.liveChangedCount=="number"&&Number.isFinite(t.liveChangedCount)?Math.max(0,Math.floor(t.liveChangedCount)):0,checkedAt:Ge(t.checkedAt,64)??""},i=Ge(t.mergedHash,64);return i&&/^[0-9a-f]+$/.test(i)&&(s.mergedHash=i),s}function ju(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e.slice(0,200)){if(!n||typeof n!="object")continue;let{nodeId:r,property:o}=n;typeof r!="string"||typeof o!="string"||t.push({nodeId:r.replace(/\0/g,"").slice(0,200),property:o.replace(/\0/g,"").slice(0,200)})}return t.length>0?t:void 0}function ma(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e.slice(0,Du)){if(!n||typeof n!="object"||Array.isArray(n))continue;let r=n;if(typeof r.code!="string"||!r.code||typeof r.error!="string"||!r.error)continue;let o={code:r.code.replace(/\0/g,"").slice(0,80),error:r.error.replace(/\0/g,"").slice(0,500)};typeof r.nodeId=="string"&&r.nodeId&&(o.nodeId=r.nodeId.replace(/\0/g,"").slice(0,200)),typeof r.type=="string"&&r.type&&(o.type=r.type.replace(/\0/g,"").slice(0,200)),typeof r.property=="string"&&r.property&&(o.property=r.property.replace(/\0/g,"").slice(0,200)),typeof r.configType=="string"&&r.configType&&(o.configType=r.configType.replace(/\0/g,"").slice(0,200)),Array.isArray(r.candidates)&&(o.candidates=r.candidates.filter(s=>typeof s=="string"&&s.length>0).slice(0,20).map(s=>s.replace(/\0/g,"").slice(0,200))),t.push(o)}return t.length>0?t:void 0}var eo,gt,ga,me,Y,jt,Re,Du,Ke,Qr,Je=q(()=>{"use strict";eo=require("crypto"),gt=require("fs"),ga=require("path");Zr();se();a(U,"isOpenProposalStatus");a(to,"oauthOwnerId");a(Ut,"proposalOwnerId");me=class extends Error{static{a(this,"ProposalNotFoundError")}code="proposal_not_found";statusCode=404;constructor(t){super(`Proposal '${t}' was not found`),this.name="ProposalNotFoundError"}},Y=class extends Error{static{a(this,"ProposalNotPendingError")}code="proposal_not_pending";statusCode=409;constructor(t,n){super(`Proposal '${t}' is '${n}', not pending`),this.name="ProposalNotPendingError"}},jt=class extends Error{static{a(this,"ProposalNotOwnedError")}code="proposal_not_owned";statusCode=403;constructor(t){super(`Proposal '${t}' was not created by this access link`),this.name="ProposalNotOwnedError"}},Re=class extends Error{static{a(this,"ProposalConcurrentApproveError")}code="proposal_approve_in_progress";statusCode=409;constructor(t){super(`Proposal '${t}' is already being approved`),this.name="ProposalConcurrentApproveError"}};a(Mu,"isProposalStatus");Du=50;a(Ge,"cleanText");a(Hu,"sanitizeRebaseInfo");Ke=class extends Error{static{a(this,"ProposalConflictError")}code="proposal_conflict";statusCode=409;conflicts;constructor(t,n){super(`Proposal '${t}' conflicts with changes made in the editor since it was submitted; resolve the conflicts before approving.`),this.name="ProposalConflictError",this.conflicts=n}};a(ju,"sanitizeRestoredSecrets");a(ma,"sanitizeProposalWarnings");Qr=class{constructor(t,n={}){this.paths=t;this.now=n.now??(()=>Date.now()),this.randomId=n.randomId??(()=>(0,eo.randomBytes)(12).toString("hex")),this.maxProposalBytes=n.maxProposalBytes??K}static{a(this,"ProposalStore")}inFlight=new Set;now;randomId;maxProposalBytes;fileFor(t){let n;try{n=Xr(t)}catch{throw new me(t)}return Ss(this.paths.proposals,`${n}.json`)}toDiskRecord(t){let n=Xr(t.id),r=We(String(t.expectedRev).slice(0,200),"expectedRev"),o=We(String(t.payload.flowId).slice(0,200),"flowId"),s=it(t.payload.flow),i=t.baseFlow!==void 0?it(t.baseFlow):void 0,d=t.createdBy.kind==="oauth"?{kind:"oauth",clientId:We(String(t.createdBy.clientId).slice(0,200),"clientId"),user:String(t.createdBy.user).replace(/\0/g,"").slice(0,200)}:{kind:"access-link",linkId:We(String(t.createdBy.linkId).slice(0,200),"linkId"),...typeof t.createdBy.label=="string"?{label:t.createdBy.label.replace(/\0/g,"").slice(0,200)}:{}},l={id:n,type:"replace_flow",status:Mu(t.status)?t.status:"pending",expectedRev:r,payload:{flowId:o,flow:s},createdBy:d,createdAt:String(t.createdAt).slice(0,64),updatedAt:String(t.updatedAt).slice(0,64)};i!==void 0&&(l.baseFlow=i),typeof t.title=="string"&&t.title.length>0&&(l.title=t.title.replace(/\0/g,"").slice(0,80).trim(),l.title||delete l.title);let c=ma(t.warnings);c&&(l.warnings=c);let u=ju(t.restoredSecrets);if(u&&(l.restoredSecrets=u),t.deployPending&&U(l.status)){let f=Ge(t.deployPending.mergedHash,64),h=Ge(t.deployPending.reason,500);f&&/^[0-9a-f]+$/.test(f)&&h&&(l.deployPending={mergedHash:f,reason:h},t.deployPending.queueReleased===!0&&(l.deployPending.queueReleased=!0))}let p=Hu(t.rebase);if((p&&U(l.status)||p&&p.state!=="conflict")&&(l.rebase=p),t.decidedBy)if(t.decidedBy.kind==="access-link"){let f={kind:"access-link",linkId:We(String(t.decidedBy.linkId).slice(0,200),"linkId")};typeof t.decidedBy.label=="string"&&(f.label=t.decidedBy.label.replace(/\0/g,"").slice(0,200)),l.decidedBy=f}else{let f={kind:"admin"};typeof t.decidedBy.username=="string"&&(f.username=t.decidedBy.username.replace(/\0/g,"").slice(0,200)),l.decidedBy=f}return typeof t.reason=="string"&&(l.reason=t.reason.replace(/\0/g,"").slice(0,2e3)),typeof t.resultRev=="string"&&(l.resultRev=We(t.resultRev.slice(0,200),"resultRev")),l}writeAtomic(t,n){let r=this.fileFor(t),o=this.toDiskRecord(n),s=ft(o,this.maxProposalBytes,{kind:"proposal",pretty:!0});cn(this.paths.root,r,s)}create(t){let n=new Date(this.now()).toISOString(),r=Xr(`prop-${this.randomId()}`),o={id:r,type:"replace_flow",status:"pending",expectedRev:t.expectedRev,payload:{flowId:t.payload.flowId,flow:t.payload.flow},baseFlow:t.baseFlow,title:t.title,createdBy:t.createdBy,createdAt:n,updatedAt:n},s=ma(t.warnings);return s&&(o.warnings=s),t.restoredSecrets?.length&&(o.restoredSecrets=t.restoredSecrets),this.writeAtomic(r,o),this.get(r)}get(t){let n=this.fileFor(t);if(!(0,gt.existsSync)(n))throw new me(t);return JSON.parse((0,gt.readFileSync)(n,"utf8"))}list(t){if(!(0,gt.existsSync)(this.paths.proposals))return[];let n=(0,gt.readdirSync)(this.paths.proposals).filter(o=>o.endsWith(".json")),r=[];for(let o of n){let s=(0,ga.basename)(o);if(!(s!==o||s.includes("..")))try{let i=JSON.parse((0,gt.readFileSync)(Ss(this.paths.proposals,s),"utf8"));if(t?.linkId&&Ut(i.createdBy)!==t.linkId||t?.status&&i.status!==t.status||t?.flowId&&i.payload.flowId!==t.flowId)continue;r.push(i)}catch{}}return r.sort((o,s)=>o.createdAt<s.createdAt?1:-1)}beginApprove(t){if(this.inFlight.has(t))throw new Re(t);let n=this.get(t);if(!U(n.status))throw new Y(t,n.status);return this.inFlight.add(t),n}releaseLock(t){this.inFlight.delete(t)}recordDeployPending(t,n){let r=this.get(t);if(!U(r.status))throw new Y(t,r.status);return n?r.deployPending=n:delete r.deployPending,r.status="pending",r.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,r),this.get(t)}markApproved(t,n,r){let o=this.get(t);return o.status="approved",delete o.deployPending,o.decidedBy=n,o.resultRev=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markRejected(t,n,r){if(this.inFlight.has(t))throw new Re(t);let o=this.get(t);return o.status="rejected",n&&(o.decidedBy=n),o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markFailed(t,n,r){let o=this.get(t);return o.status="failed",n&&(o.decidedBy=n),o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markWithdrawn(t,n,r){if(this.inFlight.has(t))throw new Re(t);let o=this.get(t);if(!U(o.status))throw new Y(t,o.status);return o.status="withdrawn",o.decidedBy=n,o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markClosed(t,n,r,o){let s=this.get(t);if(!U(s.status))throw new Y(t,s.status);return s.status=n,o&&(s.decidedBy=o),s.reason=r,s.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,s),this.get(t)}recordRebase(t,n){let r=this.get(t);if(!U(r.status))return r;let o=n.state==="conflict"?"conflict":"pending",s=r.rebase;return r.status===o&&s!==void 0&&s.rev===n.rev&&s.state===n.state&&s.mergedHash===n.mergedHash&&s.liveChangedCount===n.liveChangedCount&&JSON.stringify(s.conflicts)===JSON.stringify(n.conflicts)?r:(r.status=o,r.rebase=n,r.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,r),this.get(t))}updateBody(t,n){let r=this.get(t);if(!U(r.status))throw new Y(t,r.status);if(this.inFlight.has(t))throw new Re(t);return r.baseFlow=n.baseFlow,r.payload={flowId:r.payload.flowId,flow:n.flow},n.expectedRev!==void 0&&(r.expectedRev=n.expectedRev),n.title!==void 0&&(r.title=n.title),n.restoredSecrets!==void 0&&(n.restoredSecrets.length>0?r.restoredSecrets=n.restoredSecrets:delete r.restoredSecrets),n.warnings!==void 0&&(n.warnings.length>0?r.warnings=n.warnings:delete r.warnings),r.status="pending",delete r.rebase,r.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,r),this.get(t)}isApproving(t){return this.inFlight.has(t)}}});function ya(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new Error(`${t} must be a node object`);let n=e;if(typeof n.id!="string"||n.id.length===0)throw new Error(`${t} needs a non-empty string id`);if(typeof n.type!="string"||n.type.length===0)throw new Error(`${t} '${n.id}' needs a non-empty string type`);return n}function no(e,t,n){let r=e.find(d=>d.id===t);if(!r||r.type!=="tab"&&r.type!=="subflow")throw new X(t);let o={...r,id:t,type:r.type};delete o.nodes,delete o.configs,delete o.subflows,n.label!==void 0&&(o.label=n.label),n.disabled!==void 0&&(o.disabled=n.disabled),n.info!==void 0&&(o.info=n.info),n.env!==void 0&&(o.env=n.env);let s=[];for(let[d,l]of(n.nodes??[]).entries())s.push({...ya(l,`flowData.nodes[${d}]`),z:t});for(let[d,l]of(n.configs??[]).entries())s.push({...ya(l,`flowData.configs[${d}]`),z:t});return[...e.filter(d=>d.id!==t&&d.z!==t),o,...s]}function or(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new Error(`Expected ${t} to be an object with rev and flows`);let n=e;if(typeof n.rev!="string"||n.rev.length===0)throw new Error(`Expected ${t}.rev to be a non-empty string`);if(!Array.isArray(n.flows))throw new Error(`Expected ${t}.flows to be an array`);return{rev:n.rev,flows:n.flows}}function yn(e,t){if(Array.isArray(t)){let d=t.find(u=>u&&typeof u=="object"&&u.id===e&&(u.type==="tab"||u.type==="subflow"));if(!d)throw new X(e);let l=t.filter(u=>u&&typeof u=="object"&&u.id!==e&&u.z===e),c={id:e,type:d.type,nodes:l};return typeof d.label=="string"&&(c.label=d.label),typeof d.disabled=="boolean"&&(c.disabled=d.disabled),typeof d.info=="string"&&(c.info=d.info),c}if(!t||typeof t!="object")throw new X(e);let n=t,r=typeof n.id=="string"&&n.id.length>0?n.id:e,o=typeof n.type=="string"&&n.type.length>0?n.type:"tab",s=Array.isArray(n.nodes)?n.nodes:[],i={...n,id:r,type:o,nodes:s};return Array.isArray(n.configs)&&(i.configs=n.configs),i}var ro=q(()=>{"use strict";Te();a(ya,"asFlowNode");a(no,"replaceFlowTabInFlatFlows");a(or,"assertFlowsSnapshot");a(yn,"normalizeFlowTab")});function wa(e,t=new xs){return oo in e?e:{kind:e.kind,[oo]:t,async getFlows(){let r=await e.getFlows();return t.record(r),r},async getFlow(r){let o=await e.getFlow(r);if(!t.has(o.rev))try{t.record(await e.getFlows())}catch{}return o},async updateFlow(r){return e.updateFlow(r)}}}function Ts(e){return oo in e?e[oo]:void 0}function so(e,t){return{kind:t,async getFlows(){return{rev:e.rev,flows:JSON.parse(JSON.stringify(e.flows))}},async getFlow(n){let r=JSON.parse(JSON.stringify(e.flows)),o=r.find(l=>l.id===n&&(l.type==="tab"||l.type==="subflow"));if(!o)throw new X(n);let s=[],i=[];for(let l of r){if(l.id===n||l.z!==n)continue;(l.type!=="group"&&l.x===void 0&&l.y===void 0&&l.wires===void 0?i:s).push(l)}let d={id:n,type:o.type,nodes:s};for(let l of["label","disabled","info","env"])o[l]!==void 0&&(d[l]=o[l]);return i.length>0&&(d.configs=i),{rev:e.rev,flow:d}},async updateFlow(){throw new Error("snapshot adapter is read-only")}}}var Uu,xs,oo,Rs=q(()=>{"use strict";Te();Uu=24,xs=class{constructor(t=Uu){this.maxRevs=t}static{a(this,"RevHistory")}byRev=new Map;record(t){if(!(!t||typeof t.rev!="string"||!Array.isArray(t.flows))&&!this.byRev.has(t.rev))for(this.byRev.set(t.rev,{rev:t.rev,flows:JSON.parse(JSON.stringify(t.flows))});this.byRev.size>this.maxRevs;){let n=this.byRev.keys().next().value;if(n===void 0)break;this.byRev.delete(n)}}get(t){let n=this.byRev.get(t);return n?{rev:n.rev,flows:JSON.parse(JSON.stringify(n.flows))}:void 0}has(t){return this.byRev.has(t)}},oo=Symbol.for("ai-access.revHistory");a(wa,"withRevHistory");a(Ts,"revHistoryOf");a(so,"snapshotAdapter")});function Cs(e){return e?Bu.has(e)?!0:e.startsWith("subflow:"):!1}function yt(e,t="node.type"){if(e==null||e==="")throw new b(`${t} is required`,"missing_node_type");if(typeof e!="string")throw new b(`${t} must be a non-empty string (got ${typeof e})`,"invalid_node_type",{details:{type:e,got:typeof e}});if(!e.trim())throw new b(`${t} is required`,"missing_node_type");return e}function Vu(e,t){if(typeof e!="string"||typeof t!="string")return Number.MAX_SAFE_INTEGER;let n=e.toLowerCase(),r=t.toLowerCase();if(n===r)return 0;let o=n.length,s=r.length;if(o===0)return s;if(s===0)return o;let i=new Array(s+1),d=new Array(s+1);for(let l=0;l<=s;l++)i[l]=l;for(let l=1;l<=o;l++){d[0]=l;let c=n.charAt(l-1);for(let u=1;u<=s;u++){let p=c===r.charAt(u-1)?0:1;d[u]=Math.min((d[u-1]??0)+1,(i[u]??0)+1,(i[u-1]??0)+p)}for(let u=0;u<=s;u++)i[u]=d[u]??0}return i[s]??s}function Yu(e,t){let n=e.toLowerCase(),r=t.toLowerCase(),o=Math.min(n.length,r.length),s=0;for(;s<o&&n.charAt(s)===r.charAt(s);)s+=1;return s}function Zu(e){return Math.max(2,Math.floor(e.length/3))}function Is(e,t,n=5){if(typeof e!="string"||!e)return[];let r=Zu(e);return t.filter(s=>typeof s=="string"&&s!==e&&!Xu.has(s)&&!s.startsWith("_")&&!s.startsWith("subflow:")).map(s=>({c:s,dist:Vu(e,s),prefix:Yu(e,s)})).filter(s=>s.dist<=r).sort((s,i)=>s.dist-i.dist||i.prefix-s.prefix||s.c.localeCompare(i.c)).slice(0,n).map(s=>s.c)}function Qu(e){return e.endsWith("/")?e.slice(0,-1):e}function Ta(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,xa.createRequire)(e)}function ep(){try{let t=Ta()("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}function Ra(e){let t=e.types,n=Array.isArray(t)?t.filter(i=>typeof i=="string"&&i.length>0):[],r="";if(typeof e.module=="string"&&e.module)r=e.module;else if(typeof e.name=="string"&&e.name)r=e.name;else if(typeof e.id=="string"){let i=String(e.id).split("/")[0];r=i&&i.length>0?i:String(e.id)}let o=typeof e.id=="string"&&e.id?e.id:n.length===1&&r?`${r}/${n[0]}`:r;if(!o||n.length===0)return null;let s={id:o,module:r.length>0?r:o,types:n};return typeof e.version=="string"&&(s.version=e.version),typeof e.enabled=="boolean"&&(s.enabled=e.enabled),typeof e.name=="string"&&(s.name=e.name),s}function Ls(e=Ju,t={}){let n=t.module??"node-red",r=t.version??"4.0.0",o=[...e],s=t.configs??{};return{listNodeSets(){return Promise.resolve([{id:n,module:n,version:r,types:o,enabled:!0,name:n}])},getNodeConfigHtml(i){if(s[i])return Promise.resolve(s[i]??null);let d=i.includes("/")?i.slice(i.lastIndexOf("/")+1):i;return s[d]?Promise.resolve(s[d]??null):Promise.resolve(null)}}}function Pa(e={}){if(ep())try{let n=Ta()("@node-red/runtime");if(n.nodes&&typeof n.nodes.getNodeList=="function")return new Es(n.nodes)}catch{}if(e.baseUrl)return new _s({baseUrl:e.baseUrl,...e.accessToken?{accessToken:e.accessToken}:{},...e.fetch?{fetch:e.fetch}:{}});throw new Error("No usable NodeTypesAdapter: runtime.nodes unavailable and no HTTP baseUrl")}async function Ns(e){let t=await e.listNodeSets(),n=new Map;for(let r of t)for(let o of r.types){if(n.has(o))continue;let s={type:o,module:r.module,setId:r.id};typeof r.enabled=="boolean"&&(s.enabled=r.enabled),r.version&&(s.version=r.version),n.set(o,s)}return{byType:n,allNames:[...n.keys()]}}function tp(e,t){let n=t.toLowerCase();return[e.type,e.module,e.category??"",e.description??""].join(`
4
- `).toLowerCase().includes(n)}async function ao(e,t={}){let n=t.limit??qu,{byType:r}=await Ns(e),o=[...r.values()].sort((u,p)=>u.type.localeCompare(p.type)),s=t.q?.trim();s&&(o=o.filter(u=>tp(u,s)));let i=o.length,d=i>n,c={types:o.slice(0,n),total:i,truncated:d};return s&&(c.q=s),c}function np(e,t){let n=`data-help-name="${t}"`,r=`data-help-name='${t}'`,o=e.toLowerCase(),s=0;for(;s<e.length;){let i=e.indexOf(n,s),d=e.indexOf(r,s),l=-1;if(i>=0&&(d<0||i<d)?l=i:d>=0&&(l=d),l<0)return null;let c=o.lastIndexOf("<script",l);if(c<0||c<s){s=l+1;continue}let u=e.indexOf(">",l);if(u<0)return null;let p=o.indexOf("</script",u+1);if(p<0)return null;let f=p+8;for(;f<e.length&&/\s/.test(e.charAt(f));)f+=1;if(e.charAt(f)!==">"){s=p+1;continue}return e.slice(u+1,p)}return null}function va(e,t){let n=`<${t}`,r=`</${t}`,o="",s=0,i=e.toLowerCase();for(;s<e.length;){let d=i.indexOf(n,s);if(d<0){o+=e.slice(s);break}o+=e.slice(s,d);let l=e.indexOf(">",d);if(l<0)break;let c=i.indexOf(r,l+1);if(c<0)break;let u=c+r.length;for(;u<e.length&&/\s/.test(e.charAt(u));)u+=1;e.charAt(u)===">"?(s=u+1,o+=" "):s=c+r.length}return o}function rp(e,t=2e3){let n=va(e,"script");return n=va(n,"style"),n=n.replace(/<[^>]+>/g," ").replace(/&nbsp;/gi," ").replace(/[#*_`]+/g," ").replace(/\s+/g," ").trim(),n.length>t&&(n=`${n.slice(0,t-1)}\u2026`),n}function op(e,t){try{let n=/RED\.nodes\.registerType\s*\(\s*['"]([^'"]+)['"]\s*,\s*\{/.exec(e);if(!n?.[1])return null;let r=n[1];if(t&&r!==t){let o=/RED\.nodes\.registerType\s*\(\s*['"]([^'"]+)['"]\s*,\s*\{/g,s,i=null,d=-1;for(;(s=o.exec(e))!==null;)if(s[1]===t){i=s[1],d=s.index;break}return!i||d<0?null:ka(e,d,i)}return ka(e,n.index,r)}catch{return null}}function ka(e,t,n){let r=e.slice(t),o=r.indexOf("{");if(o<0)return{type:n};let s=sr(r.slice(o));if(!s)return{type:n};let i=Aa(s,"category"),d=Aa(s,"color"),l=Sa(s,"inputs"),c=Sa(s,"outputs"),u=ip(s),p=[];if(u){for(let g of Object.values(u))if(g&&typeof g=="object"&&!Array.isArray(g)){let y=g.type;typeof y=="string"&&y.length>0&&p.push(y)}}let f,h=np(e,n);h&&(f=rp(h));let m={type:n};return i&&(m.category=i),d&&(m.color=d),l!==void 0&&(m.inputs=l),c!==void 0&&(m.outputs=c),u&&(m.defaults=u),p.length>0&&(m.requiredConfigNodeTypes=[...new Set(p)]),f&&(m.help=f),m}function sp(e){let t=e.trim();if(!t)return{};if(!/\n(?=#{1,3}\s*(inputs?|outputs?)\b)/i.test(`
5
- ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(let l of r){let c=l.trim();if(!c)continue;let u=/^(#{1,3})\s*(inputs?|outputs?)\b[\s:]*\n?([\s\S]*)$/i.exec(c);if(u){let p=(u[2]??"").toLowerCase(),f=(u[3]??"").trim();p.startsWith("input")?s=f||c:i=f||c}else o||(o=c.replace(/^#{1,3}\s*/,"").trim())}let d={};return o&&(d.summary=o),s&&(d.inputs=s),i&&(d.outputs=i),!d.summary&&!d.inputs&&!d.outputs&&(d.text=t),d}function sr(e){if(!e.startsWith("{"))return null;let t=0,n=null,r=!1;for(let o=0;o<e.length;o++){let s=e[o];if(n){if(r){r=!1;continue}if(s==="\\"){r=!0;continue}s===n&&(n=null);continue}if(s==='"'||s==="'"||s==="`"){n=s;continue}if(s==="{")t+=1;else if(s==="}"&&(t-=1,t===0))return e.slice(0,o+1)}return null}function Aa(e,t){let n=new RegExp(`\\b${t}\\s*:\\s*['"]([^'"]+)['"]`);return e.match(n)?.[1]}function Sa(e,t){let n=new RegExp(`\\b${t}\\s*:\\s*(-?\\d+(?:\\.\\d+)?)`),r=e.match(n);if(!r?.[1])return;let o=Number(r[1]);return Number.isFinite(o)?o:void 0}function ip(e){let t=e.search(/\bdefaults\s*:/);if(t<0)return;let n=e.slice(t),r=n.indexOf("{");if(r<0)return;let o=sr(n.slice(r));if(!o)return;let s={},i=/([a-zA-Z_$][\w$]*)\s*:\s*\{/g,d;for(;(d=i.exec(o))!==null;){let l=d[1];if(!l||l==="defaults")continue;let c=sr(o.slice(d.index+d[0].length-1));if(!c)continue;let u={},p=/\btype\s*:\s*['"]([^'"]+)['"]/.exec(c);p?.[1]&&(u.type=p[1]),/\brequired\s*:\s*true\b/.test(c)&&(u.required=!0);let f=/\bvalue\s*:\s*/.exec(c);if(f){let h=c.slice(f.index+f[0].length).trimStart();u.value=lp(h)}s[l]=u}return Object.keys(s).length>0?s:void 0}function ap(e,t){let n=e.charAt(t);if(n==="u"&&/^[0-9a-fA-F]{4}/.test(e.slice(t+1,t+5)))return{char:String.fromCharCode(Number.parseInt(e.slice(t+1,t+5),16)),next:t+5};if(n==="x"&&/^[0-9a-fA-F]{2}/.test(e.slice(t+1,t+3)))return{char:String.fromCharCode(Number.parseInt(e.slice(t+1,t+3),16)),next:t+3};let r;switch(n){case"n":r=`
6
- `;break;case"r":r="\r";break;case"t":r=" ";break;case"b":r="\b";break;case"f":r="\f";break;case"v":r="\v";break;case"0":r="\0";break;case"\\":r="\\";break;case"'":r="'";break;case'"':r='"';break;case"/":r="/";break;default:r=n;break}return{char:r,next:t+1}}function dp(e){return e==="_DEFAULT_"?"":e}function io(e){let t=e.trimStart();if(t.startsWith("'")||t.startsWith('"')){let r=t[0],o=1,s="";for(;o<t.length;){let i=t[o];if(i==="\\"&&o+1<t.length){let d=ap(t,o+1);s+=d.char,o=d.next;continue}if(i===r)break;s+=i,o+=1}return dp(s)}if(t.startsWith("[")){let r=_a(t);if(!r)return;let o=r.slice(1,-1).trim();if(!o)return[];let s=[],i=0;for(;i<o.length;){for(;i<o.length&&/[\s,]/.test(o.charAt(i));)i+=1;if(i>=o.length)break;let d=o.slice(i),l=io(d);s.push(l);let c=Ps(d);i+=c>0?c:1}return s}if(t.startsWith("{")){let r=sr(t);if(!r)return;let o=r.slice(1,-1);if(!o.trim())return{};let s={},i=0;for(;i<o.length;){for(;i<o.length&&/[\s,]/.test(o.charAt(i));)i+=1;if(i>=o.length)break;let d;if(o[i]==="'"||o[i]==='"'){let c=io(o.slice(i));d=typeof c=="string"?c:"",i+=Ps(o.slice(i))}else{let c=/^[a-zA-Z_$][\w$]*/.exec(o.slice(i));if(!c)break;d=c[0],i+=d.length}for(;i<o.length&&/\s/.test(o.charAt(i));)i+=1;if(o[i]!==":")break;for(i+=1;i<o.length&&/\s/.test(o.charAt(i));)i+=1;let l=o.slice(i);s[d]=io(l),i+=Ps(l)}return s}let n=/^-?\d+(?:\.\d+)?/.exec(t);if(n)return Number(n[0]);if(t.startsWith("true"))return!0;if(t.startsWith("false"))return!1;if(t.startsWith("null"))return null}function Ps(e){let t=e.trimStart(),n=e.length-t.length;if(t.startsWith("'")||t.startsWith('"')){let o=t[0],s=1;for(;s<t.length;){if(t[s]==="\\"&&s+1<t.length){s+=2;continue}if(t[s]===o)return n+s+1;s+=1}return n+t.length}if(t.startsWith("[")){let o=_a(t);return n+(o?.length??1)}if(t.startsWith("{")){let o=sr(t);return n+(o?.length??1)}let r=/^(?:-?\d+(?:\.\d+)?|true|false|null)/.exec(t);return r?n+r[0].length:n+1}function _a(e){if(!e.startsWith("["))return null;let t=0,n=null,r=!1;for(let o=0;o<e.length;o++){let s=e[o];if(n){if(r){r=!1;continue}if(s==="\\"){r=!0;continue}s===n&&(n=null);continue}if(s==="'"||s==='"'){n=s;continue}if(s==="[")t+=1;else if(s==="]"&&(t-=1,t===0))return e.slice(0,o+1)}return null}function lp(e){return io(e)}function cp(e,t){for(let n of e)if(n.types.includes(t)){let r=n.id.includes("/")||n.types.length===1?n.id:`${n.module}/${t}`;return{set:n,setId:r}}return null}function Fs(e,t){let n=e.toLowerCase();return t.includes(e)?e:t.find(r=>r.toLowerCase()===n)}async function Bt(e,t,n={}){if(!t||typeof t!="string")throw new b("Missing node type","missing_node_type");let r=n.includeHelp!==!1;if(Cs(t)){let l={type:t,module:"node-red",category:"config",inputs:0,outputs:0,defaults:null};return r&&(l.help={summary:"Built-in structural Node-RED type (not a palette node)."}),l}let o=await e.listNodeSets(),s=o.flatMap(l=>l.types),i=cp(o,t);if(!i){let l=Fs(t,s);throw l&&l!==t?new b(`Node type '${t}' was not found \u2014 did you mean '${l}'? Types are case-sensitive.`,"node_type_not_found",{statusCode:404,details:{type:t,didYouMean:l,caseSensitive:!0,nearest:[l]}}):new b(`Node type '${t}' was not found`,"node_type_not_found",{statusCode:404,details:{type:t,nearest:Is(t,s,5)}})}let d={type:t,module:i.set.module,setId:i.setId,inputs:1,outputs:1,defaults:null};if(i.set.version&&(d.version=i.set.version),typeof i.set.enabled=="boolean"&&(d.enabled=i.set.enabled),typeof e.getNodeConfigHtml=="function")try{let l=await e.getNodeConfigHtml(i.setId)??await e.getNodeConfigHtml(`${i.set.module}/${t}`)??await e.getNodeConfigHtml(t);if(l){let c=op(l,t);c&&(c.category&&(d.category=c.category),c.color&&(d.color=c.color),d.inputs=c.inputs??d.inputs,d.outputs=c.outputs??d.outputs,c.defaults&&Object.keys(c.defaults).length>0?d.defaults=c.defaults:d.defaults=null,c.requiredConfigNodeTypes&&(d.requiredConfigNodeTypes=c.requiredConfigNodeTypes),r&&c.help&&(d.help=sp(c.help)))}}catch{}return d}async function Ea(e,t){let{allNames:n}=await Ns(e),r=new Set(n),o=new Set,s=a(l=>{let c=yt(l,"node.type");if(o.has(c)||Cs(c)){o.add(c);return}if(o.add(c),r.has(c))return;let u=Fs(c,n);throw u&&u!==c?new b(`Unknown node type '${c}' \u2014 did you mean '${u}'? Types are case-sensitive.`,"unknown_node_type",{details:{type:c,didYouMean:u,caseSensitive:!0,nearest:[u]}}):new b(`Unknown node type '${c}'. Call node-types / palette search; ask the user to install via Manage palette, then recheck.`,"unknown_node_type",{details:{type:c,nearest:Is(c,n,5)}})},"checkType"),i=a(l=>{if(!l||typeof l!="object"||Array.isArray(l))return;let c=l;typeof c.id=="string"&&c.id.length>0&&s(c.type)},"visitNodeOrConfig");a(l=>{if(!l||typeof l!="object"||Array.isArray(l))return;let c=l;if(c.type!==void 0&&s(c.type),Array.isArray(c.nodes))for(let u of c.nodes)i(u);if(Array.isArray(c.configs))for(let u of c.configs)i(u)},"visitTab")(t)}async function Ca(e,t){let n=yt(t,"node.type");if(Cs(n))return;let{allNames:r}=await Ns(e);if(r.includes(n))return;let o=Fs(n,r);throw o&&o!==n?new b(`Unknown node type '${n}' \u2014 did you mean '${o}'? Types are case-sensitive.`,"unknown_node_type",{details:{type:n,didYouMean:o,caseSensitive:!0,nearest:[o]}}):new b(`Unknown node type '${n}'. Call node-types / palette search; ask the user to install via Manage palette, then recheck.`,"unknown_node_type",{details:{type:n,nearest:Is(n,r,5)}})}async function up(e,t){let n=new Headers({Accept:"application/json"});t&&n.set("If-None-Match",t);let r=await fetch(e,{headers:n}),o=r.headers.get("etag")??void 0;if(r.status===304)return{status:304,...o?{etag:o}:{}};if(!r.ok)return{status:r.status,body:null,...o?{etag:o}:{}};let s=await r.json();return{status:r.status,body:s,...o?{etag:o}:{}}}async function pp(e,t){let n=Date.now();if(Ne&&n-Ne.fetchedAt<Ku)return Ne.body;let r=Ne?.etag,o=await e(t,r);if(o.status===304&&Ne)return Ne={...Ne,fetchedAt:n,...o.etag?{etag:o.etag}:{}},Ne.body;if(!o.body||!Array.isArray(o.body.modules)){if(Ne)return Ne.body;throw new b("Node-RED catalogue unavailable","catalogue_unavailable",{statusCode:502})}return Ne={fetchedAt:n,body:o.body,...o.etag?{etag:o.etag}:{}},o.body}async function lo(e,t,n={}){let r=t.trim();if(!r)throw new b("q query parameter is required","missing_query");let o=n.limit??zu,s=n.catalogueUrl??Gu,i=n.catalogueFetcher??up,d=await pp(i,s),l=Array.isArray(d.modules)?d.modules:[],c=await e.listNodeSets(),u=new Set(c.flatMap(g=>{let y=[g.module,g.id];return g.id.includes("/")&&y.push(g.id.split("/")[0]??g.module),y.map(A=>A.toLowerCase())})),p=r.toLowerCase(),f=[];for(let g of l){let y=g.id.toLowerCase(),A=(g.keywords??[]).map(D=>String(D).toLowerCase()),x=(g.description??"").toLowerCase(),$=fp(p,y,A,x);if($<=0)continue;let L={module:g.id,installed:u.has(y)};typeof g.description=="string"&&(L.description=g.description),typeof g.version=="string"&&(L.version=g.version),typeof g.updated_at=="string"&&(L.updated=g.updated_at),typeof g.downloads=="number"&&(L.downloads=g.downloads),typeof g.url=="string"&&(L.link=g.url),f.push({item:L,score:$})}f.sort((g,y)=>y.score-g.score||(y.item.downloads??0)-(g.item.downloads??0)||g.item.module.localeCompare(y.item.module));let h=f.length,m=h>o;return{q:r,modules:f.slice(0,o).map(g=>g.item),total:h,truncated:m,hint:Wu}}function fp(e,t,n,r){if(!e)return 0;if(t===e||t===`node-red-contrib-${e}`||t===`node-red-node-${e}`)return 1e4;let o=0;t.startsWith(`node-red-contrib-${e}`)||t.startsWith(`node-red-node-${e}`)?o+=500:t.startsWith(e)?o+=80:t.includes(e)&&(o+=60);for(let s of n)s===e?o+=50:s.startsWith(e)||e.startsWith(s)?o+=35:s.includes(e)&&(o+=25);return r.includes(e)&&(o+=10),o}var xa,Bu,qu,zu,Wu,Gu,Ku,Ju,Xu,_s,ba,Es,Ne,qt=q(()=>{"use strict";xa=require("module");ie();Bu=new Set(["tab","subflow","group","junction"]),qu=100,zu=30,Wu="Recommend only \u2014 ask the user to install via Manage palette, then recheck node-types. This API never installs. Core Node-RED nodes (module node-red) are not listed in the public catalogue; use node-types for installed core types.",Gu="https://catalogue.nodered.org/catalogue.json",Ku=3600*1e3,Ju=["inject","debug","function","template","comment","change","switch","delay","trigger","link in","link out","link call","mqtt in","mqtt out","http in","http response","http request","websocket in","websocket out","tcp in","tcp out","udp in","udp out","catch","status","complete","join","split","sort","batch","csv","html","json","xml","yaml","file","file in","watch","exec","range","rbe"];a(Cs,"isStructuralNodeType");a(yt,"requireNodeTypeName");a(Vu,"editDistance");a(Yu,"sharedPrefixLength");Xu=new Set(["unknown","tab","subflow","group"]);a(Zu,"nearestDistanceCutoff");a(Is,"nearestNodeTypes");a(Qu,"trimTrailingSlash");a(Ta,"createPluginRequire");a(ep,"isRuntimeSettingsAvailable");a(Ra,"normalizeNodeSet");a(Ls,"createStaticNodeTypesAdapter");_s=class{static{a(this,"HttpNodeTypesAdapter")}baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=Qu(t.baseUrl),this.accessToken=t.accessToken,this.fetchImpl=t.fetch??globalThis.fetch.bind(globalThis)}headers(t){let n=new Headers;return n.set("Accept",t),this.accessToken&&n.set("Authorization",`Bearer ${this.accessToken}`),n}async listNodeSets(){let t=await this.fetchImpl(`${this.baseUrl}/nodes`,{headers:this.headers("application/json")});if(!t.ok)throw new Error(`Admin API GET /nodes failed (${t.status})`);let n=await t.json();if(!Array.isArray(n))throw new Error("Admin API GET /nodes returned non-array JSON");let r=[];for(let o of n){if(!o||typeof o!="object"||Array.isArray(o))continue;let s=Ra(o);s&&r.push(s)}return r}async getNodeConfigHtml(t,n){let r=t.split("/").map(i=>encodeURIComponent(i)).join("/"),o=n?`?lang=${encodeURIComponent(n)}`:"",s=await this.fetchImpl(`${this.baseUrl}/nodes/${r}${o}`,{headers:this.headers("text/html")});return s.status===404||!s.ok?null:s.text()}},ba={user:null,req:null},Es=class{constructor(t){this.nodes=t}static{a(this,"RedNodeTypesAdapter")}async listNodeSets(){if(typeof this.nodes.getNodeList!="function")return[];let t,n=this.nodes.getNodeList;try{t=await Promise.resolve(n(ba))}catch{try{t=await Promise.resolve(n())}catch{return[]}}if(!Array.isArray(t))return[];let r=[];for(let o of t){if(!o||typeof o!="object"||Array.isArray(o))continue;let s=Ra(o);s&&r.push(s)}return r}async getNodeConfigHtml(t,n){if(typeof this.nodes.getNodeConfig!="function")return null;try{let r=await Promise.resolve(this.nodes.getNodeConfig({id:t,lang:n??"en-US",...ba}));if(typeof r=="string"&&r.length>0)return r}catch{}try{let r=await Promise.resolve(this.nodes.getNodeConfig(t,n));return typeof r=="string"&&r.length>0?r:null}catch{return null}}};a(Pa,"createNodeTypesAdapter");a(Ns,"flattenInstalledTypes");a(tp,"matchesQuery");a(ao,"listNodeTypes");a(np,"extractHelpScriptBody");a(va,"removeHtmlBlocks");a(rp,"stripHelpText");a(op,"parseRegisterTypeFromHtml");a(ka,"parseRegisterTypeAt");a(sp,"splitHelpSections");a(sr,"extractBalancedObject");a(Aa,"matchStringProp");a(Sa,"matchNumberProp");a(ip,"parseDefaultsBlock");a(ap,"unescapeJsStringEscape");a(dp,"normalizeDefaultValue");a(io,"parseJsLiteral");a(Ps,"measureJsLiteralLength");a(_a,"extractBalancedArray");a(lp,"parseLooseValue");a(cp,"findSetForType");a(Fs,"findCaseInsensitiveType");a(Bt,"getNodeTypeDetail");a(Ea,"assertKnownNodeTypes");a(Ca,"assertKnownNodeType");Ne=null;a(up,"defaultCatalogueFetcher");a(pp,"loadCatalogue");a(lo,"searchPalette");a(fp,"paletteMatchScore")});function at(e){if(e.length===0)return;let t=e[0];if(!t)return;let n=e.map(s=>{let i={...s};return delete i.statusCode,i});if(e.length>1)throw new b(`${e.length} validation errors`,"validation_failed",{statusCode:400,details:{errors:n,errorCount:e.length}});let r={errors:n};for(let[s,i]of Object.entries(t))s==="code"||s==="error"||s==="errors"||s==="statusCode"||i!==void 0&&(r[s]=i);let o=typeof t.statusCode=="number"?t.statusCode:400;throw new b(t.error,t.code,{statusCode:o,details:r})}function Os(e,t="payload.flow"){let n=new Map;for(let o of e)for(let s of o){if(!s||typeof s!="object"||Array.isArray(s))continue;let i=s.id;typeof i=="string"&&i.length>0&&n.set(i,(n.get(i)??0)+1)}let r=[];for(let[o,s]of n)s>1&&r.push({code:"duplicate_node_id",error:`Node id '${o}' appears ${s} times in ${t}; node ids must be unique`,nodeId:o,count:s});return r}function hp(e){let t=typeof e.label=="string"&&e.label?` '${e.label}'`:"";return e.type==="tab"?`tab${t||` '${e.id}'`}`:e.type==="subflow"?`subflow${t||` '${e.id}'`}`:typeof e.z=="string"&&e.z?`${e.type} node on '${e.z}'`:`global ${e.type} config node`}function La(e,t,n){let r=new Map;for(let i of n)!i||typeof i.id!="string"||i.id===t||i.z===t||r.set(i.id,i);let o=[],s=new Set;for(let i of[e.nodes,e.configs])if(Array.isArray(i))for(let d of i){let l=d&&typeof d=="object"?d.id:void 0;if(typeof l!="string"||!l||s.has(l))continue;if(s.add(l),l===t){o.push({code:"duplicate_node_id",error:`Node id '${l}' equals the tab id; node ids must be unique across all flows`,nodeId:l});continue}let c=r.get(l);c&&o.push({code:"duplicate_node_id",error:`Node id '${l}' is already used by ${hp(c)}; node ids must be unique across all flows`,nodeId:l})}return o}function Ia(e){let t=new Map;for(let n of[e.nodes,e.configs])if(Array.isArray(n))for(let r of n){if(!r||typeof r!="object"||Array.isArray(r))continue;let o=r;typeof o.id=="string"&&t.set(o.id,o)}return t}function gp(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]);for(let r of n)if(!mp.has(r)&&JSON.stringify(e[r])!==JSON.stringify(t[r]))return!0;return!1}function yp(e,t){let n=Ia(t);if(!e)return new Set(n.keys());let r=Ia(e),o=new Set;for(let[s,i]of n){let d=r.get(s);(!d||gp(d,i))&&o.add(s)}return o}function Na(e,t){let n=[],r=e;typeof r.id!="string"||r.id.length===0?n.push({code:"missing_flow_id",error:"payload.flow.id is required and must match payload.flowId"}):r.id!==t&&n.push({code:"invalid_flow_id",error:`payload.flow.id ('${r.id}') must match payload.flowId ('${t}')`}),"nodes"in r?Array.isArray(r.nodes)||n.push({code:"invalid_nodes",error:"payload.flow.nodes must be an array"}):n.push({code:"invalid_nodes",error:"payload.flow.nodes must be an array (use [] to clear the tab intentionally)"}),"configs"in r&&r.configs!==void 0&&!Array.isArray(r.configs)&&n.push({code:"invalid_nodes",error:"payload.flow.configs must be an array"});let o=[];Array.isArray(r.nodes)&&o.push({key:"nodes",list:r.nodes}),Array.isArray(r.configs)&&o.push({key:"configs",list:r.configs}),n.push(...Os(o.map(s=>s.list)));for(let{key:s,list:i}of o)for(let d=0;d<i.length;d+=1){let l=i[d];if(!l||typeof l!="object"||Array.isArray(l)){n.push({code:"invalid_node",error:`payload.flow.${s}[${d}] must be an object`});continue}let c=l;(typeof c.id!="string"||c.id.length===0)&&n.push({code:"missing_node_id",error:`payload.flow.${s}[${d}] needs a non-empty string id`});try{yt(c.type,"node.type")}catch(u){u instanceof b&&n.push({code:u.code,error:u.message,...typeof c.id=="string"?{nodeId:c.id}:{}})}"wires"in c&&wn(c.wires,n,typeof c.id=="string"?c.id:void 0)}return n}function wn(e,t,n){if(!Array.isArray(e))return t.push({code:"invalid_wires",error:"wires must be an array of output ports (each a list of node ids)",...n?{nodeId:n}:{}}),!1;for(let r of e){if(!Array.isArray(r))return t.push({code:"invalid_wires",error:"wires must be an array of output ports (each a list of node ids)",...n?{nodeId:n}:{}}),!1;for(let o of r)if(typeof o!="string"||o.length===0)return t.push({code:"invalid_wires",error:"wires targets must be non-empty node id strings",...n?{nodeId:n}:{}}),!1}return!0}async function wp(e,t){if(typeof e.outputs=="number"&&Number.isFinite(e.outputs)&&e.outputs>=0)return Math.trunc(e.outputs);let n=typeof e.type=="string"?e.type:"";if(!(!t||!n))try{return(await Bt(t,n,{includeHelp:!1})).outputs}catch{return}}function bp(e,t,n){let r=typeof e.type=="string"?e.type:"node";if(!(n.length<=t))return t===0?{code:"invalid_wires",error:`${r} has 0 outputs; wires must be []`,nodeId:e.id,type:r}:{code:"invalid_wires",error:`${r} has ${t} outputs; wires has ${n.length} port arrays (max ${t})`,nodeId:e.id,type:r}}function vp(e){return Array.isArray(e)&&e.some(t=>typeof t=="string"&&t.length>0)}function kp(e,t){return t<=0?!1:!("wires"in e)||e.wires===void 0?!0:Array.isArray(e.wires)?!e.wires.some(vp):!1}async function $s(e,t,n={}){let r=[],o=[],s=new Set(e.map(d=>d.id)),i=n.softWarningNodeIds;for(let d of e){let l=await wp(d,t),c=typeof d.type=="string"?d.type:"node",u=!i||i.has(d.id);if("wires"in d&&d.wires!==void 0){if(!wn(d.wires,r,d.id))continue;let p=d.wires;if(l!==void 0){let f=bp(d,l,p);f?(f.cause="excess_ports",r.push(f)):u&&p.length<l&&o.push({code:"incomplete_wires",error:`${c} '${d.id}' declares ${l} outputs but wires has only ${p.length} port(s)`,nodeId:d.id,type:c})}for(let f of p)for(let h of f){let m=String(h);s.has(m)||r.push({code:"invalid_wires",error:`Wire target '${m}' on node '${d.id}' was not found in this flow (add the node in the same patch or use an existing id)`,nodeId:d.id,cause:"missing_target",targetId:m})}}u&&l!==void 0&&kp(d,l)&&o.push({code:"unwired_node",error:`Node '${d.id}' (${c}) has ${l} output(s) but is not wired to anything yet`,nodeId:d.id,type:c})}return{errors:r,warnings:o}}function Ms(e,t){let n=[];for(let r of e)r.z===void 0||r.z===""||typeof r.z=="string"&&r.z!==t&&n.push({code:"z_mismatch",error:`Node '${r.id}' has z='${r.z}' but flow id is '${t}'`,nodeId:r.id});return n}function Ap(e,t){return e.filter(n=>n.type===t).map(n=>n.id)}async function Ds(e,t,n){let r=[],o=[...t];for(let s of e){let i;try{i=yt(s.type,"node.type")}catch{continue}let d;try{d=await Bt(n,i,{includeHelp:!1})}catch{continue}let l=d.defaults;if(!l||typeof l!="object")continue;let c=new Set(d.requiredConfigNodeTypes??[]);for(let[u,p]of Object.entries(l)){if(!p||typeof p!="object"||Array.isArray(p))continue;let f=p.type;if(typeof f!="string"||!f)continue;let m=p.required===!0||c.has(f),g=s[u],y=Ap(o,f);if(typeof g!="string"||g.length===0){m&&r.push({code:"missing_config_node",error:`Node '${s.id}' (${i}) has no ${f} on '${u}' yet \u2014 configure before deploy if needed`,nodeId:s.id,type:i,property:u,configType:f,candidates:y});continue}o.find(x=>x.id===g)?.type!==f&&r.push({code:"missing_config_node",error:`Node '${s.id}' property '${u}' does not resolve to a ${f} config yet`,nodeId:s.id,type:i,property:u,configType:f,candidates:y})}}return r}async function Fa(e,t={}){let n=t.flowId??(typeof e.id=="string"?e.id:""),r=Array.isArray(e.nodes)?e.nodes:[],o=Array.isArray(e.configs)?e.configs:[],s=[],i=[];n&&s.push(...Ms(r,n));let d=t.baseFlow?yp(t.baseFlow,e):void 0,l=await $s(r,t.nodeTypes,{...d?{softWarningNodeIds:d}:{}});if(s.push(...l.errors),i.push(...l.warnings),t.nodeTypes){let c=await Ds(r,o,t.nodeTypes);d?i.push(...c.filter(u=>!u.nodeId||d.has(u.nodeId))):i.push(...c)}return{errors:s,warnings:i}}var mp,Hs=q(()=>{"use strict";ie();qt();a(at,"throwValidationIssues");a(Os,"collectDuplicateNodeIdIssues");a(hp,"describeLiveNode");a(La,"collectCrossFlowDuplicateIssues");mp=new Set(["x","y","w","h","g","id","z"]);a(Ia,"collectNodesMap");a(gp,"nodeContentChanged");a(yp,"changedNodeIds");a(Na,"collectFullFlowShapeIssues");a(wn,"assertValidWiresShape");a(wp,"resolveNodeOutputCount");a(bp,"wireCountIssue");a(vp,"portHasTargets");a(kp,"nodeIsUnwired");a($s,"collectWireValidation");a(Ms,"collectZMismatchIssues");a(Ap,"configCandidates");a(Ds,"collectConfigWarnings");a(Fa,"validateFlowTab")});function wt(e,t="node"){return typeof e=="string"&&e.trim()?e.trim():t}function co(e){let t=new Map;for(let n of e)n.type==="tab"&&typeof n.id=="string"&&typeof n.label=="string"&&n.label.trim()&&t.set(n.id,n.label.trim());return t}function $a(e,t){e[Oa]=t}function Tp(e){if(!e)return;let t=e[Oa];return t instanceof Map?t:void 0}function Rp(e){return typeof e!="string"||!e.trim()?new Set:new Set(e.split(",").map(t=>t.trim()).filter(Boolean))}function Pp(e){if(e===!0||e===1)return!0;if(typeof e=="string"){let t=e.trim().toLowerCase();return t==="1"||t==="true"||t==="yes"}return!1}function ge(e){let t=Rp(e.include);return Pp(e.include_payload)&&(t.add("payload"),t.add("baseFlow")),t}function V(e,t){let n={req:e};t!==void 0&&(n.httpAdminRoot=t);let r=Tp(e);return r&&(n.liveFlowLabels=r),n}function bn(e){return typeof e.name=="string"&&e.name.trim()?e.name.trim():typeof e.label=="string"&&e.label.trim()?e.label.trim():""}function ir(e){return`"${e.replace(/"/g,"'")}"`}function vn(e){let t=new Map;for(let n of e.nodes??[])t.set(n.id,n);for(let n of e.configs??[])t.set(n.id,n);return t}function _p(e,t){let n=new Set(["x","y","w","h","g","id","z"]),r=new Set([...Object.keys(e),...Object.keys(t)]);for(let o of r)if(!n.has(o)&&JSON.stringify(e[o])!==JSON.stringify(t[o]))return!0;return!1}function Ep(e,t){let n=bn(e),r=bn(t);if(!n||!r||n===r)return!1;let o=new Set(["x","y","w","h","g","id","z","name","label"]),s=new Set([...Object.keys(e),...Object.keys(t)]);for(let i of s)if(!o.has(i)&&JSON.stringify(e[i])!==JSON.stringify(t[i]))return!1;return!0}function js(e,t){let n=[],r=[],o=[];for(let[s,i]of t)e.has(s)||n.push(i);for(let[s,i]of e)t.has(s)||r.push(i);for(let[s,i]of t){let d=e.get(s);d&&_p(d,i)&&o.push({before:d,after:i})}return{added:n,removed:r,modified:o}}function uo(e){return e===1?"node":"nodes"}function Cp(e){return e.length===1&&e[0]?`adds 1 ${wt(e[0].type)}`:`adds ${e.length} ${uo(e.length)}`}function Ip(e){return`deletes ${e} ${uo(e)}`}function Lp(e){return`edits ${e} ${uo(e)}`}function Np(e){let t=[];e.removed>0&&t.push(Ip(e.removed));let n=e.added??[],r=e.addedCount??n.length;r>0&&t.push(n.length===r?Cp(n):`adds ${r} ${uo(r)}`),(e.modified??0)>0&&t.push(Lp(e.modified??0));let o=t.length?t.join(", "):"replaces nodes",s=e.flowLabel?.trim();return s?`Replace ${s}: ${o}`:`Replace flow: ${o}`}function Fp(e){let{added:t,removed:n,modified:r}=e;if(t.length+n.length+r.length!==1)return;if(t[0]){let u=t[0],p=wt(u.type),f=bn(u);return f?`Add ${p} node ${ir(f)}`:`Add ${p} node`}if(n[0]){let u=n[0],p=wt(u.type),f=bn(u);return f?`Remove ${p} node ${ir(f)}`:`Remove ${p} node`}let s=r[0];if(!s)return;let i=wt(s.after.type,wt(s.before.type)),d=bn(s.before),l=bn(s.after);if(Ep(s.before,s.after)&&d&&l)return`Rename ${i} node ${ir(d)} \u2192 ${ir(l)}`;let c=l||d;return c?`Edit ${i} ${ir(c)}`:`Edit ${i}`}function Us(e,t){return typeof e?.label=="string"&&e.label.trim()?e.label.trim():typeof t?.label=="string"&&t.label.trim()?t.label.trim():""}function po(e,t){if(typeof e.flowLabel=="string"&&e.flowLabel.trim())return e.flowLabel.trim();let n=Us(e.payload?.flow,e.baseFlow);if(n)return n;let r=e.payload?.flowId;return r&&t?.has(r)?t.get(r)??"":""}function Ma(e,t){let n=t.trim(),r=e.trim();if(!n||!r||r.includes(n))return r;let o=r.lastIndexOf(" in ");if(o>=0){let s=r.slice(o+4);if(s.length>0&&n.startsWith(s))return r}return`${r} in ${n}`}function Bs(e,t){let n=js(vn(e??{}),vn(t)),r=n.added.length+n.removed.length+n.modified.length,o=Us(t,e);if(r===0)return o?`Update ${o}`:"Update flow";if(n.removed.length>0)return Np({...o?{flowLabel:o}:{},removed:n.removed.length,added:n.added,modified:n.modified.length});if(r>xp)return o?`${r} changes in ${o}`:`${r} changes`;let s=Fp(n)??(o?`Update ${o}`:"Update flow");return Ma(s,o)}function Op(e,t){let n=(e??"").trim();return!!(!n||n.includes("[object Object]")||/^Update(\s|$)/i.test(n)||t>0&&!/^Replace\b/i.test(n))}function qs(e,t){return`${e} \xB7 ${Mp(t)}`}function $p(e){let t=e>=0?"+":"-",n=Math.abs(e),r=String(Math.floor(n/60)).padStart(2,"0"),o=String(n%60).padStart(2,"0");return`${t}${r}:${o}`}function Mp(e,t=new Date(e)){let n=Number.isNaN(t.getTime())?new Date(e):t;if(Number.isNaN(n.getTime()))return e.slice(0,16);let r=n.getDate(),s=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"][n.getMonth()]??"Jan",i=String(n.getHours()).padStart(2,"0"),d=String(n.getMinutes()).padStart(2,"0"),l=$p(-n.getTimezoneOffset());return`${r} ${s} ${i}:${d} ${l}`}function ar(e){if(typeof e!="string")return;let t=e.replace(/\0/g,"").replace(/[\u0001-\u001F\u007F]/g," ").replace(/\s+/g," ").trim().slice(0,Sp).trim();return t.length>0?t:void 0}function Dp(e,t){let n=Ae(t);return`${n==="/"?"/ai-access":`${n}/ai-access`}#proposal=${encodeURIComponent(e)}`}function zs(e,t={}){let n=po(e,t.liveFlowLabels),r=ar(e.title),o=Bs(e.baseFlow,n&&!Us(e.payload.flow,e.baseFlow)?{...e.payload.flow,label:n}:e.payload.flow),s=js(vn(e.baseFlow??{}),vn(e.payload.flow)),i=Op(r,s.removed.length)?o:r??o;return Ma(i,n)}function Hp(e){return typeof e.storedTitle=="string"&&e.storedTitle?e.storedTitle:ar(e.title)}function pe(e,t={}){let n=zs(e,t),r=po(e,t.liveFlowLabels),o=Hp(e)??n,s=Dp(e.id,t.httpAdminRoot),i=t.req?rr(t.req,s):s;return{...e,title:n,storedTitle:o,flowLabel:r,displayName:qs(n,e.createdAt),reviewUrl:i,reviewPath:s}}function jp(e){let t=js(vn(e.baseFlow??{}),vn(e.payload.flow)),n=[];return t.added.length>0&&n.push(t.added.length===1?"Adds 1 node":`Adds ${t.added.length} nodes`),t.removed.length>0&&n.push(t.removed.length===1?"Deletes 1 node":`Deletes ${t.removed.length} nodes`),t.modified.length>0&&n.push(t.modified.length===1?"Edits 1 node":`Edits ${t.modified.length} nodes`),n.length===0?"No node changes":n.map((r,o)=>o===0?r:r.charAt(0).toLowerCase()+r.slice(1)).join(", ")}function Up(e){if(e.kind==="oauth")return{kind:"oauth",clientId:e.clientId,user:e.user};let t={kind:e.kind,linkId:e.linkId};return typeof e.label=="string"&&e.label.length>0&&(t.label=e.label),t}function fe(e,t={}){let n=pe(e,t),r=t.include??new Set,o={id:n.id,status:n.status,type:n.type,title:n.title,storedTitle:n.storedTitle,displayName:n.displayName,flowLabel:n.flowLabel,reviewUrl:n.reviewUrl,reviewPath:n.reviewPath,createdAt:n.createdAt,updatedAt:n.updatedAt,expectedRev:n.expectedRev,createdBy:Up(n.createdBy),changeSummary:jp(e)};return r.has("payload")&&(o.payload=n.payload),r.has("baseFlow")&&n.baseFlow!==void 0&&(o.baseFlow=n.baseFlow),n.warnings&&n.warnings.length>0&&(o.warnings=n.warnings),typeof e.payload.flowId=="string"&&(o.flowId=e.payload.flowId),e.restoredSecrets&&e.restoredSecrets.length>0&&(o.restoredSecrets=e.restoredSecrets),n.deployPending&&(o.deployPending=n.deployPending),n.rebase&&(o.rebase=n.rebase),n.baseRevStale!==void 0&&(o.baseRevStale=n.baseRevStale),n.decidedBy!==void 0&&(o.decidedBy=n.decidedBy),n.reason!==void 0&&(o.reason=n.reason),n.resultRev!==void 0&&(o.resultRev=n.resultRev),o}function Da(e,t={}){return e.map(n=>fe(n,t))}var Sp,xp,Oa,Fe=q(()=>{"use strict";ve();Be();Sp=80,xp=1;a(wt,"nodeTypeText");Oa=Symbol.for("ai-access.liveFlowLabels");a(co,"liveFlowLabelsFromFlows");a($a,"setRequestFlowLabels");a(Tp,"getRequestFlowLabels");a(Rp,"parseIncludeQuery");a(Pp,"isTruthyFlag");a(ge,"resolveIncludeFromQuery");a(V,"presentOptionsFor");a(bn,"nodeLabel");a(ir,"quoteName");a(vn,"collectNodes");a(_p,"nodeContentChanged");a(Ep,"isRenameOnly");a(js,"classifyFlowChanges");a(uo,"pluralNodes");a(Cp,"describeAddedClause");a(Ip,"describeRemovedClause");a(Lp,"describeModifiedClause");a(Np,"buildReplaceFlowTitle");a(Fp,"describeOneChange");a(Us,"flowLabelHint");a(po,"resolveFlowLabel");a(Ma,"ensureTitleIncludesFlowName");a(Bs,"autoGenerateProposalTitle");a(Op,"shouldRefreshDisplayTitle");a(qs,"buildProposalDisplayName");a($p,"formatUtcOffset");a(Mp,"formatProposalSubmittedAt");a(ar,"sanitizeProposalTitle");a(Dp,"buildProposalReviewPath");a(zs,"resolveProposalTitle");a(Hp,"storedTitleOf");a(pe,"presentProposal");a(jp,"buildChangeSummary");a(Up,"presentCreatedByForApi");a(fe,"presentProposalForApi");a(Da,"presentProposalsForApi")});function Ha(e){let t=new Map;for(let n of e.nodes??[])t.set(n.id,n);for(let n of e.configs??[])t.set(n.id,n);return t}function qp(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let o of n)Bp.has(o)||JSON.stringify(e[o])!==JSON.stringify(t[o])&&r.push(o);return r.sort()}function ja(e){let t=Ha(e.baseFlow??{}),n=Ha(e.payload.flow),r=new Map;for(let[o,s]of n){let i=t.get(o);if(!i){r.set(o,new Set(["*"]));continue}let d=qp(i,s);d.length>0&&r.set(o,new Set(d))}for(let[o]of t)n.has(o)||r.set(o,new Set(["*"]));return r}function zp(e,t){let n=[],r=[];for(let[o,s]of e){let i=t.get(o);if(!i)continue;n.push(o);let d=[];for(let c of s){if(c==="*"){d.push(...[...i].filter(u=>u!=="*")),i.has("*")&&d.push("*");continue}(i.has(c)||i.has("*"))&&d.push(c)}let l=[...new Set(d)].sort();l.length>0&&r.push({nodeId:o,fields:l})}return n.sort(),{nodeIds:n,fields:r}}function Ve(e,t,n){let r=ja(t),o=[];for(let s of e.list()){if(s.id===t.id||!U(s.status)||s.payload.flowId!==t.payload.flowId)continue;let i=zs(s),d=n?Ut(s.createdBy)===n:Ut(s.createdBy)===Ut(t.createdBy),l=zp(r,ja(s));if(l.nodeIds.length===0)continue;let c={id:s.id,title:i,displayName:qs(i,s.createdAt),mine:d};l.nodeIds.length>0&&(c.overlappingNodeIds=l.nodeIds),l.fields.length>0&&(c.overlappingFields=l.fields),o.push(c)}return{conflictingProposals:o}}function Ye(e){let t={};return e.conflictingProposals.length>0&&(t.conflictingProposals=e.conflictingProposals,t.warning="Other pending proposals touch the same flow \u2014 approve order may conflict (like a git merge)."),t}function ue(e){return e>0?{autoClosedCount:e}:{}}var Bp,zt=q(()=>{"use strict";Je();Fe();Bp=new Set(["x","y","w","h","g","id","z"]);a(Ha,"collectNodes");a(qp,"changedFieldKeys");a(ja,"computeProposalTouches");a(zp,"overlapBetween");a(Ve,"findConflictingProposals");a(Ye,"conflictWarningFields");a(ue,"autoClosedFields")});function kn(e){let t=e.join("\0"),n=fo.get(t);if(n)return n;let r=[...new Set(e.map(d=>jr(d).join("")).filter(Boolean))],o=new Map,s={patterns:[...e],matches(d){if(typeof d!="string"||d.length===0||r.length===0)return!1;let l=o.get(d);if(l!==void 0)return l;let c=i(d);return o.size>4096&&o.clear(),o.set(d,c),c}};function i(d){{let l=jr(d),c=l[l.length-1];if(!c||Wp.has(c))return!1;for(let u of r){let p=`${u}S`;for(let f=0;f<l.length;f++){let h="";for(let g=f;g<l.length;g++){if(h+=l[g],h===u||h===p)return!0;if(h.length>p.length)break}let m=l[f]??"";if(u.length>=6&&(m.endsWith(u)||m.endsWith(p)))return!0}}return!1}}return a(i,"matchUncached"),fo.size>32&&fo.clear(),fo.set(t,s),s}function dt(){return ta()?.getEffective()?.secretNamePatterns??Ft}function cr(e){let t=e.trim();return!(!t||t===J||Gp.has(t.toLowerCase())||/^\$\{[A-Za-z0-9_]+\}$/.test(t))}function Jp(e){let t=qa.get(e);return t||(t=Vp(e),qa.set(e,t)),t}function Vp(e){return[{hints:["Buffer.from","btoa"],re:/\b(?:Buffer\.from|btoa)\([ \t]{0,8}(?<q>["'`])[^"'`:\s]{1,256}:(?<s>[^"'`\s]{1,1024})\k<q>/dg,accept:a(t=>!t.includes("${"),"accept")},{hints:["PRIVATE KEY-----"],re:/-----BEGIN ((?:[A-Z0-9]{1,16} ){0,4})PRIVATE KEY-----(?<s>[^-]{1,16384})-----END \1PRIVATE KEY-----/dg},{hints:["AKIA","ASIA","ABIA","ACCA"],re:/(?<![A-Za-z0-9])(?<s>(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16})(?![A-Za-z0-9])/dg},{hints:["ghp_","gho_","ghu_","ghs_","ghr_"],re:/(?<![A-Za-z0-9_])(?<s>gh[pousr]_[A-Za-z0-9]{36,255})(?![A-Za-z0-9_])/dg},{hints:["github_pat_"],re:/(?<![A-Za-z0-9_])(?<s>github_pat_[A-Za-z0-9_]{22,255})(?![A-Za-z0-9_])/dg},{hints:["xox"],re:/(?<![A-Za-z0-9_-])(?<s>xox[abposr]-[A-Za-z0-9-]{10,512})/dg},{hints:["hooks.slack.com"],re:/hooks\.slack\.com\/services\/(?<s>[A-Za-z0-9_/]{16,512})/dg},{hints:["sk-"],re:/(?<![A-Za-z0-9_-])(?<s>sk-[A-Za-z0-9_-]{20,512})/dg},{hints:["k_live_","k_test_"],re:/(?<![A-Za-z0-9_])(?<s>[sr]k_(?:live|test)_[A-Za-z0-9]{16,512})/dg},{hints:["AIza"],re:/(?<![A-Za-z0-9_-])(?<s>AIza[0-9A-Za-z_-]{35})(?![A-Za-z0-9_-])/dg},{hints:["eyJ"],re:/(?<![A-Za-z0-9_-])(?<s>eyJ[A-Za-z0-9_-]{5,8192}\.[A-Za-z0-9_-]{5,8192}\.[A-Za-z0-9_-]{5,8192})/dg},{hints:["earer"],re:/\b[Bb]earer[ \t]{1,8}(?<s>(?=[A-Za-z0-9\-._~+/]{0,4096}[0-9\-._~+/])[A-Za-z0-9\-._~+/]{16,4096}={0,3})/dg},{hints:["asic"],re:/\b[Bb]asic[ \t]{1,8}(?<s>(?=[A-Za-z0-9+/]{0,4096}[0-9+/])[A-Za-z0-9+/]{8,4096}={0,2})(?![A-Za-z0-9+/])/dg},{hints:["@"],re:/\b[A-Za-z][A-Za-z0-9+.-]{1,20}:\/\/[^\s:@/'"`<>]{1,256}:(?<s>[^\s@/'"`<>]{1,1024})@/dg,accept:a(t=>!/^(?:\$\{|\{\{|<|%)/.test(t)&&t!==J,"accept")},{hints:["?","&"],re:/[?&](?<k>[A-Za-z_][\w.-]{0,63})=(?<s>[^&#\s"'`<>]{1,2048})/dg,accept:a((t,n)=>e.matches(n.groups?.k??"")&&cr(ef(t)),"accept")},{hints:[":","="],re:/(?<![\w$-])(?<q>["'`]?)(?<k>[A-Za-z_$][\w$-]{0,63})\k<q>\]?[ \t]{0,8}(?::|=(?![=>]))[ \t]{0,8}(?<vq>["'`])(?<s>(?:\\.|(?!\k<vq>)[^\\\r\n]){1,1024})\k<vq>/dg,accept:a((t,n)=>{let r=n.groups?.k??"";return!e.matches(r)||n.groups?.vq==="`"&&t.includes("${")||t.length<3||/\s/.test(t)?!1:cr(t)},"accept")}]}function Xp(e){let t=new Map;for(let r of e)t.set(r,(t.get(r)??0)+1);let n=0;for(let r of t.values()){let o=r/e.length;n-=o*Math.log2(o)}return n}function Zp(e){return/[^A-Za-z]/.test(e)||e.length>=16?!0:[/[a-z]/,/[A-Z]/,/[0-9]/,/[^A-Za-z0-9]/].filter(n=>n.test(e)).length>=3||Xp(e)>=3}function Qp(e){let t=e.patterns.filter(r=>Yp.includes(r.toUpperCase())),n=kn(t);return{hints:[":","="],re:/(?<![\w$.-])(?<k>[A-Za-z_][\w-]{0,63})[ \t]{0,8}(?::(?!\/\/)|=)[ \t]{0,8}(?<s>[^\s;,'"`&<>(){}[\]]{3,1024})/dg,accept:a((r,o)=>e.matches(o.groups?.k??"")&&cr(r)&&!r.startsWith("$")&&!r.startsWith("{{")&&(n.matches(o.groups?.k??"")||Zp(r))&&!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/.test(r)&&o.input.charAt(o.indices?.groups?.s?.[1]??0)!=="(","accept")}}function ef(e){try{return decodeURIComponent(e)}catch{return e}}function tf(e,t,n){let r=e.indexOf("=",t);if(r<=t||r>=n||r-t>48)return;let o=e.slice(t,r),s=Math.max(o.lastIndexOf('"'),o.lastIndexOf("'"),o.lastIndexOf("`")),i=o.slice(s+1).trim();if(!/^[A-Za-z][\w -]{0,40}$/.test(i))return;let d=r+1;for(;d<n&&(e[d]===" "||e[d]===" ");)d++;let l=d;for(;l<n&&!"\"'`\r".includes(e[l]??"");)l++;for(;l>d&&(e[l-1]===" "||e[l-1]===" ");)l--;return{k:i,vs:d,ve:l}}function nf(e,t){if(!e.includes(";")||!e.includes("="))return[];let n=[],r=0;for(;r<=e.length;){let o=e.indexOf(`
7
- `,r);o<0&&(o=e.length);let s=[],i=r,d=!1;for(;i<o;){let l=e.indexOf(";",i);l<0||l>o?l=o:d=!0;let c=tf(e,i,l);c&&s.push(c),i=l+1}if(d&&s.length>=2)for(let l of s)l.ve<=l.vs||!t.matches(l.k)||cr(e.slice(l.vs,l.ve))&&n.push({start:l.vs,end:l.ve});r=o+1}return n}function rf(e,t,n={}){if(e.length===0)return{text:e,secrets:[],spans:[]};let r=[],o=Jp(t);if(!n.code){let u=za.get(t);u||(u=Qp(t),za.set(t,u)),o=[...o,u]}r.push(...nf(e,t));for(let u of o){if(u.hints&&!u.hints.some(f=>e.includes(f)))continue;u.re.lastIndex=0;let p;for(;(p=u.re.exec(e))!==null;){let f=p.indices?.groups?.s,h=p.groups?.s;f&&h!==void 0&&h.length>0&&(!u.accept||u.accept(h,p))&&r.push({start:f[0],end:f[1]}),p[0].length===0&&u.re.lastIndex++}}if(r.length===0)return{text:e,secrets:[],spans:[]};r.sort((u,p)=>u.start-p.start||p.end-u.end);let s=[];for(let u of r){let p=s[s.length-1];p&&u.start<p.end?p.end=Math.max(p.end,u.end):s.push({...u})}let i="",d=0,l=[],c=[];for(let u of s){let p=e.slice(u.start,u.end);p!==J&&(i+=e.slice(d,u.start)+J,l.push(p),c.push(u),d=u.end)}return i+=e.slice(d),{text:i,secrets:l,spans:c}}function Vs(e,t){let n=[],r=0,o=a(s=>{let i=wo(s);for(let d=0;d<i;d++)n.push({kind:"literal",value:J})},"literals");for(let s of t)o(e.slice(r,s.start)),n.push({kind:"secret",value:e.slice(s.start,s.end)}),r=s.end;return o(e.slice(r)),n}function Z(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function of(e){return Z(e)&&typeof e.id=="string"&&typeof e.type=="string"}function yo(e,t){let n=new Gs(t,e.id),r=e[dr];if(Array.isArray(r))for(let s of r)typeof s=="string"&&n.paths.push(s);let o={};for(let[s,i]of Object.entries(e))s!==dr&&(Ka.has(s)?o[s]=i:Ya.has(s)&&Array.isArray(i)?o[s]=i.map(d=>Ys(d,t)):s==="env"&&Array.isArray(i)?o[s]=n.env(i,"env"):o[s]=n.value(i,s,s,e));return n.paths.length>0&&(o[dr]=n.paths),o}function Ys(e,t){return Array.isArray(e)?e.map(n=>Ys(n,t)):of(e)?yo(e,t):e}function bt(e,t=dt()){let n={matcher:kn(t),count:0};return{value:Ys(e,n),count:n.count}}function Ks(e){if(Array.isArray(e)){let t=e.map(n=>({item:Ks(n),key:vt(n)}));return t.every(n=>n.key!==void 0)&&new Set(t.map(n=>n.key)).size===t.length&&t.sort((n,r)=>(n.key??"").localeCompare(r.key??"")),t.map(n=>n.item)}return Z(e)?Object.fromEntries(Object.entries(e).map(([t,n])=>[t,Ks(n)])):e}function Xa(e,t){let n=a(s=>{let i=new Map;return yo(Ks(s),{matcher:kn(dt()),count:0,secrets:i}),i},"values"),r=n(e),o=n(t);return[...new Set([...r.keys(),...o.keys()])].some(s=>JSON.stringify(r.get(s))!==JSON.stringify(o.get(s)))}function Xs(e,t=dt()){return e.map(n=>{if(n.nodeName===void 0)return{...n};let r=bt({id:"conflict",type:"node",name:n.nodeName},t);return{...n,nodeName:r.value.name}})}function lt(e,t=dt()){let n={...e,payload:{...e.payload,flow:bt(e.payload.flow,t).value}};return e.baseFlow!==void 0&&(n.baseFlow=bt(e.baseFlow,t).value),e.rebase&&(n.rebase={...e.rebase,conflicts:Xs(e.rebase.conflicts,t)}),n}function kt(e,t=dt){if(Za(e))return e;let n=t===dt,r=n?Wa.get(e):void 0;if(r)return r;let o={kind:e.kind,[Zs]:e,async getFlows(){let s=await e.getFlows();return{rev:s.rev,flows:bt(s.flows,t()).value}},async getFlow(s){let i=await e.getFlow(s);return{rev:i.rev,flow:bt(i.flow,t()).value}},updateFlow:a(s=>e.updateFlow(s),"updateFlow")};return n&&Wa.set(e,o),o}function Za(e){return Zs in e}function Oe(e){return Za(e)?e[Zs]:e}function Xe(e,t){return e.accessLink||e.auth?kt(t):t}function wo(e){let t=0,n=e.indexOf(J);for(;n!==-1;)t++,n=e.indexOf(J,n+J.length);return t}function ur(e){let t=e.split(J),n=[];for(let r=1;r<t.length;r++){let o=t[r-1]??"",s=t[r]??"",i=o.lastIndexOf(`
3
+ `,o=Buffer.from(r,"utf8");if(o.byteLength>t)throw new ce(n.kind,o.byteLength,t);return{bytes:o}}function na(e,t){let n=(0,Yn.resolve)(t),r=(0,Yn.resolve)(e);if(r!==n&&!r.startsWith(n+Yn.sep))throw new Error("Path escapes .ai-access storage root");return r}function un(e,t,n){let r=na(t,e),o=`${r}.${process.pid}.tmp`;(0,it.writeFileSync)(o,n.bytes,{mode:Hr}),(0,it.renameSync)(o,r),(0,it.chmodSync)(r,Hr)}function ra(e,t,n){let r=na(t,e);(0,it.appendFileSync)(r,n.bytes,{mode:Hr});try{(0,it.chmodSync)(r,Hr)}catch{}}var it,Yn,ys,Hr,K,ta,ce,oe=q(()=>{"use strict";it=require("fs"),Yn=require("path"),ys=448,Hr=384,K=15e5,ta=8192,ce=class extends Error{constructor(n,r,o){super(`${n} size ${r} bytes exceeds limit of ${o} bytes`);this.kind=n;this.size=r;this.maxBytes=o;this.name="StoragePayloadTooLargeError"}static{a(this,"StoragePayloadTooLargeError")}code="payload_too_large";statusCode=413};a(ht,"encodeTrustedJson");a(na,"assertUnderAiAccess");a(un,"writeTrustedAtomic");a(ra,"appendTrusted")});function Zn(e){if(typeof e!="string"||e.length===0||e.length>2048)return;let t;try{t=new URL(e)}catch{return}if(!(t.protocol!=="http:"&&t.protocol!=="https:")&&!(t.username||t.password||t.search||t.hash)&&!(t.pathname!=="/"||/^[a-z][a-z0-9+.-]*:\/\/[^/]*\//i.test(e)))return t.origin.toLowerCase()}function uu(e,t){let n=e.getHeader("Vary"),r=String(n??"").split(",").map(o=>o.trim()).filter(Boolean);r.some(o=>o.toLowerCase()===t.toLowerCase())||r.push(t),e.setHeader("Vary",r.join(", "))}function sa(e){return(t,n,r)=>{if(!au.test(t.path)){r();return}for(let c of n.getHeaderNames())c.startsWith("access-control-")&&n.removeHeader(c);let o=n.setHeader.bind(n),s=!1;n.setHeader=((c,u)=>!s&&/^access-control-/i.test(c)?n:o(c,u));let i=Zn(t.get("origin")),d=i===void 0?void 0:e().find(c=>c===i),l=d!==void 0;if(uu(n,"Origin"),!l){if(t.method==="OPTIONS"&&t.get("access-control-request-method")){n.status(204).end();return}r();return}if(s=!0,n.setHeader("Access-Control-Allow-Origin",d),n.setHeader("Access-Control-Expose-Headers",cu),t.method==="OPTIONS"&&t.get("access-control-request-method")){n.setHeader("Access-Control-Allow-Methods",du),n.setHeader("Access-Control-Allow-Headers",lu),n.setHeader("Access-Control-Max-Age",String(600)),n.status(204).end();return}s=!1,r()}}function ia(e,t){let n=e.parent,r=n&&typeof n.use=="function"?n:e;r.use(t);let o=r,i=(o._router??o.router)?.stack;if(!Array.isArray(i)||i.length<2)return;let d=i.pop(),l=0;for(;l<i.length;){let c=i[l].name;if(c!=="query"&&c!=="expressInit")break;l+=1}i.splice(l,0,d)}var au,du,lu,cu,Br=q(()=>{"use strict";au=/^\/mcp(?:\/?$|\/(?:a(?:\/|$)|api(?:\/|$)|rpc\/?$))/,du="GET, POST, DELETE, OPTIONS",lu="Authorization, Content-Type, Accept, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID",cu="Mcp-Session-Id, WWW-Authenticate, Retry-After, RateLimit, RateLimit-Policy, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset";a(Zn,"canonicalOrigin");a(uu,"appendVary");a(sa,"aiCorsMiddleware");a(ia,"installAiCorsMiddleware")});function zr(e){return e.replace(/([a-z])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/([A-Za-z])([0-9])/g,"$1 $2").replace(/([0-9])([A-Za-z])/g,"$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean).map(t=>t.toUpperCase())}function er(e){if(!Array.isArray(e))return;let t=[];for(let n of e){if(typeof n!="string")continue;let r=n.trim().toUpperCase();if(!(!qr.test(r)||t.includes(r))&&(t.push(r),t.length>=Qn))break}return t}var J,Ot,qr,Qn,pn=q(()=>{"use strict";J="[REDACTED]",Ot=Object.freeze(["PASSWORD","PASSWD","PASS","PWD","PW","PASSPHRASE","SECRET","CLIENT_SECRET","TOKEN","API_KEY","APIKEY","KEY","PRIVATE","CREDENTIAL","AUTH","AUTHORIZATION","BEARER","SESSION","COOKIE","CONNECTION_STRING","CONN_STR","CONN","DSN"]),qr=/^[A-Za-z0-9_-]{2,40}$/,Qn=100;a(zr,"tokenizeName");a(er,"normalizeSecretNamePatterns")});function fu(e){return e==="nodes"||e==="flows"||e==="full"}function Wr(e,t){if(!e||typeof e!="object"||Array.isArray(e))return{};let n=e,r={};if(typeof n.enabled=="boolean"&&(r.enabled=n.enabled),typeof n.allowWithoutAdminAuth=="boolean"&&(r.allowWithoutAdminAuth=n.allowWithoutAdminAuth),typeof n.defaultLinkTtlSeconds=="number"&&Number.isInteger(n.defaultLinkTtlSeconds)&&n.defaultLinkTtlSeconds>0&&(r.defaultLinkTtlSeconds=Math.floor(n.defaultLinkTtlSeconds)),typeof n.proposalTtlDays=="number"&&Number.isInteger(n.proposalTtlDays)&&n.proposalTtlDays>=1&&n.proposalTtlDays<=365&&(r.proposalTtlDays=n.proposalTtlDays),fu(n.defaultDeploymentType)&&(r.defaultDeploymentType=n.defaultDeploymentType),typeof n.maxActiveLinks=="number"&&Number.isInteger(n.maxActiveLinks)&&n.maxActiveLinks>0&&(r.maxActiveLinks=Math.floor(n.maxActiveLinks)),typeof n.publicBaseUrl=="string")try{r.publicBaseUrl=mn(n.publicBaseUrl)}catch{t?.(`AI Access: invalid publicBaseUrl value ${JSON.stringify(n.publicBaseUrl)} was dropped`)}if(Array.isArray(n.corsAllowedOrigins)){let o=new Set;for(let s of n.corsAllowedOrigins){let i=Zn(s);i?o.add(i):t?.(`AI Access: invalid CORS origin value ${JSON.stringify(s)} was dropped`)}r.corsAllowedOrigins=[...o]}for(let o of["rateLimitWindowMs","rateLimitMaxPerIp","rateLimitMaxPerToken"]){let s=n[o],i=o==="rateLimitWindowMs"?100:1,d=o==="rateLimitWindowMs"?36e5:1e6;typeof s=="number"&&Number.isInteger(s)&&s>=i&&s<=d&&(r[o]=s)}if(n.secretNamePatterns!==void 0){let o=er(n.secretNamePatterns);o?(Array.isArray(n.secretNamePatterns)&&o.length<n.secretNamePatterns.length&&t?.("AI Access: invalid or duplicate secretNamePatterns entries were dropped"),r.secretNamePatterns=o):t?.("AI Access: secretNamePatterns must be a list of strings; ignored")}if(n.oauth&&typeof n.oauth=="object"&&!Array.isArray(n.oauth)){let o=n.oauth;r.oauth={},typeof o.enabled=="boolean"&&(r.oauth.enabled=o.enabled);for(let i of["accessTokenTtlSec","refreshIdleTtlSec","dcrMaxClients"]){let d=o[i];typeof d=="number"&&Number.isInteger(d)&&d>0&&d<=31536e3&&(r.oauth[i]=d)}let s=o.refreshMaxLifetimeSec;typeof s=="number"&&Number.isInteger(s)&&s>=0&&s<=31536e4&&(r.oauth.refreshMaxLifetimeSec=s)}return r}function hu(e,t){if(!(0,nr.existsSync)(e))return{};try{let n=(0,nr.readFileSync)(e,"utf8");return Wr(JSON.parse(n),t)}catch(n){return t?.(`AI Access: config.json is corrupt or unreadable; defaults are used (${String(n)})`),{}}}function Gr(e,t){let n={},r={...tr};for(let s of Object.keys(tr).filter(i=>i!=="oauth"))n[s]=e[s]!==void 0?"settings.js":t[s]!==void 0?"disk":"default",Object.assign(r,{[s]:e[s]??t[s]??tr[s]});let o={...r,sources:n};return e.oauth!==void 0||t.oauth!==void 0?(o.oauth={...fn,...t.oauth??{},...e.oauth??{}},n.oauth=e.oauth!==void 0?"settings.js":"disk"):(o.oauth={...fn},n.oauth="default"),o}function vs(e,t){let n=Wr(e??{},t);for(let o of Object.keys(tr).filter(s=>s!=="oauth"))e&&Object.hasOwn(e,o)&&n[o]===void 0&&Object.assign(n,{[o]:tr[o]});let r=e?.oauth;if(r&&typeof r=="object"&&!Array.isArray(r)){let o=n.oauth??={};for(let s of Object.keys(fn))Object.hasOwn(r,s)&&o[s]===void 0&&(t?.(`AI Access: settings.js aiAccess.oauth.${s} is invalid; using the default (${String(fn[s])})`),Object.assign(o,{[s]:fn[s]}))}return n}var nr,bs,pu,fn,tr,hn,Kr=q(()=>{"use strict";nr=require("fs"),bs=require("path");ve();Br();pn();oe();pu=16384,fn={enabled:!1,accessTokenTtlSec:3600,refreshIdleTtlSec:2592e3,refreshMaxLifetimeSec:7776e3,dcrMaxClients:5},tr={enabled:!0,allowWithoutAdminAuth:!1,defaultLinkTtlSeconds:14400,proposalTtlDays:7,maxActiveLinks:10,defaultDeploymentType:"nodes",publicBaseUrl:"",corsAllowedOrigins:[],rateLimitWindowMs:6e4,rateLimitMaxPerIp:600,rateLimitMaxPerToken:300,secretNamePatterns:[...Ot],oauth:{...fn}};a(fu,"isDeploymentType");a(Wr,"sanitizePartial");a(hu,"readDiskFile");a(Gr,"mergeAiAccessSettings");a(vs,"settingsJsPartial");hn=class{constructor(t,n,r){this.paths=t;this.filePath=(0,bs.join)(t.root,"config.json"),this.settingsJs=vs(n,r),this.disk=hu((0,nr.existsSync)(this.filePath)?this.filePath:(0,bs.join)(t.root,"settings.json"),r),this.cached=Gr(this.settingsJs,this.disk)}static{a(this,"SettingsStore")}filePath;disk;settingsJs;cached;getEffective(){return this.cached}getDisk(){return{...this.disk}}update(t){let n=Wr(t);for(let r of Object.keys(this.settingsJs))delete n[r];return this.persist(n),this.disk=n,this.cached=Gr(this.settingsJs,this.disk),this.cached}patch(t,n=[]){if(t.defaultLinkTtlSeconds!==void 0&&(!Number.isInteger(t.defaultLinkTtlSeconds)||t.defaultLinkTtlSeconds<1))throw new Error("defaultLinkTtlSeconds must be an integer with a minimum of 1");if(t.proposalTtlDays!==void 0&&(!Number.isInteger(t.proposalTtlDays)||t.proposalTtlDays<1||t.proposalTtlDays>365))throw new Error("proposalTtlDays must be an integer between 1 and 365");let r=Wr(t),o={...this.disk,...r,...r.oauth?{oauth:{...this.disk.oauth,...r.oauth}}:{}};for(let s of n)delete o[s];return this.update(o)}persist(t){let n=ht(t,pu,{kind:"settings",pretty:!0});un(this.paths.root,this.filePath,n)}}});function Vr(e){Jr=e}function aa(){return Jr}function re(e){return e.adminAuth!=null&&e.adminAuth!==!1}function Ce(e){return Jr?Jr.getEffective():Gr(vs(e.aiAccess),{})}function Ee(e){let t=Ce(e);return t.enabled?re(e)?!0:t.allowWithoutAdminAuth:!1}var Jr,ke=q(()=>{"use strict";Kr();Jr=null;a(Vr,"bindSettingsStore");a(aa,"getSettingsStore");a(re,"isAdminAuthEnabled");a(Ce,"resolveAiAccessSettings");a(Ee,"areAiFeaturesEnabled")});function Ae(e){let t=`/${e??""}`.replace(/\/{2,}/g,"/");for(;t.length>1&&t.endsWith("/");)t=t.slice(0,-1);return t}function xe(e){let t=Ae(e);return t==="/"?"/mcp/api/v1":`${t}/mcp/api/v1`}function rr(e){let t=Ae(e);return t==="/"?"/ai-access":`${t}/ai-access`}function mt(e){let t=Ae(e);return t==="/"?"/mcp/rpc":`${t}/mcp/rpc`}function mu(e){let t=e.authInfo?.scope;if(t)return Array.isArray(t)?t:[t];let n=e.user?.permissions;return n?Array.isArray(n)?n:[n]:[]}function gt(e){if(e.auth)return e.auth.scopes.includes("propose")?"propose":e.auth.scopes.includes("read")?"read":null;let t=mu(e);return t.includes("*")?"*":t.includes("read")?"read":null}function gu(e,t){return!t||!e?{view:!0,manageLinks:!1,approveProposals:!1}:e==="*"?{view:!0,manageLinks:!0,approveProposals:!0}:{view:!0,manageLinks:!1,approveProposals:!1}}function da(e,t={error:"Authentication required",code:"auth_required"},n){let r=e.end.bind(e),o=!0;return e.end=((...s)=>o&&e.statusCode===401&&!e.headersSent?(o=!1,e.removeHeader("Content-Length"),e.setHeader("Content-Type","application/json; charset=utf-8"),n&&e.setHeader("WWW-Authenticate",n),r(JSON.stringify(t))):r(...s)),()=>{o=!1,e.end=r}}function Se(e,t){let n="read";return(r,o,s)=>{if(!re(e.settings)){s();return}let i=t==="api"?da(o):null;e.auth.needsPermission(n)(r,o,d=>{if(i?.(),d){s(d);return}if(!r.user){if(t==="page"){o.redirect(Ae(e.settings.httpAdminRoot));return}o.status(401).json({error:"Authentication required",code:"auth_required"});return}if(!gt(r)){o.status(401).json({error:"Insufficient permissions",code:"insufficient_permissions"});return}s()})}}function Yr(e,t){let n=re(e.settings),r=Ce(e.settings),o=Ee(e.settings),s=null;return n?s=gt(t):o&&(s="*"),{adminAuthEnabled:n,aiFeaturesEnabled:o,allowWithoutAdminAuth:r.allowWithoutAdminAuth,permission:s,capabilities:gu(s,o)}}function gn(e){return(t,n,r)=>{if(!Ee(e.settings)){n.status(403).json({error:"AI Access features are disabled. Enable adminAuth, or set aiAccess.allowWithoutAdminAuth to true for local-only use.",code:"ai_features_disabled"});return}r()}}function yn(e){return(t,n,r)=>{if(!re(e.settings)){if(Ee(e.settings)){r();return}n.status(403).json({error:"AI Access features are disabled",code:"ai_features_disabled"});return}if(gt(t)!=="*"){n.status(403).json({error:"Full admin permission (*) is required for this action",code:"insufficient_permissions"});return}r()}}function yu(e){return e.startsWith("aia_")}function wu(e){return yu(e)?!0:e.length<24}function bu(e){if(e.length<24||e.startsWith("aia_")&&e.length<40)return"The token may be truncated \u2014 ask the user for the full access-link token."}function la(e,t,n,r){if(!re(e.settings)){r();return}let o={error:Ie,code:at},s=da(n,o,fe);e.auth.needsPermission("read")(t,n,i=>{if(s(),i){r(i);return}if(!n.headersSent){if(!t.user||!gt(t)){n.setHeader("WWW-Authenticate",fe),n.status(401).json(o);return}r()}})}function ca(e,t,n,r,o){let s=e.headers.authorization;if(typeof s!="string"||!/^Bearer\s+\S+/i.test(s.trim()))return"continue";let d=/^Bearer\s+(\S+)$/i.exec(s.trim())?.[1];if(!d)return"continue";let l=n(d);if(l.status==="active")return l.link.kind!==r?(ir(t,r,l.link.kind),"wrong_kind"):(e.accessLink=l.link,"ok");if(l.status==="expired")return sr(e,t,o,l.expiresAt),"invalid";if(wu(d)){t.setHeader("WWW-Authenticate",fe);let c=bu(d);return t.status(401).json({error:$t,code:or,...c?{hint:c}:{}}),"invalid"}return"continue"}function ua(e,t,n="rest"){return(r,o,s)=>{if(!Ee(e.settings)){o.status(403).json({error:"AI Access features are disabled. Enable adminAuth, or set aiAccess.allowWithoutAdminAuth to true for local-only use.",code:"ai_features_disabled"});return}let i=ca(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||la(e,r,o,s)}}function pa(e,t,n="rest"){return(r,o,s)=>{let i=ca(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||la(e,r,o,s)}}var ze=q(()=>{"use strict";Mt();ke();a(Ae,"normalizeHttpAdminRoot");a(xe,"mcpApiBase");a(rr,"dashboardPath");a(mt,"mcpRpcPath");a(mu,"scopesFromRequest");a(gt,"resolvePermission");a(gu,"capabilitiesFor");a(da,"interceptNeedsPermissionUnauthorized");a(Se,"requireAdminSession");a(Yr,"describeAccess");a(gn,"requireAiFeatures");a(yn,"requireStarPermission");a(yu,"looksLikeAccessLinkToken");a(wu,"looksLikeFailedAccessLinkAttempt");a(bu,"truncatedTokenHint");a(la,"continueAsAdminSessionOrAccessLinkMissing");a(ca,"tryAccessLinkBearer");a(ua,"requireAdminReadOrAccessLink");a(pa,"requireAdminSessionOrAccessLink")});function vu(e,t){let n=Ae(e);return`${n==="/"?"/mcp/a":`${n}/mcp/a`}/${t}`}function ku(e){return!!e.app.get("trust proxy")}function fa(e,t){let n=e.get(t);if(typeof n!="string"||n.length===0)return;let r=n.split(",")[0]?.trim();return r&&r.length>0?r:void 0}function Au(e){if(typeof e!="string"||e.trim().length===0)return;let t;try{t=new URL(e.trim())}catch{return}if(!(t.protocol!=="http:"&&t.protocol!=="https:"))return`${t.origin}${t.pathname.replace(/\/+$/,"")}`}function Dt(e){try{let n=new URL(`http://${e.includes(":")&&!e.startsWith("[")?`[${e}]`:e}`).hostname.replace(/^\[|\]$/g,"");if(!(0,ks.isIP)(n))return;if(n.startsWith("::ffff:")){let r=n.slice(7);if((0,ks.isIP)(r)===4)return r;let o=r.split(":");if(o.length===2){let s=parseInt(o[0],16)*65536+parseInt(o[1],16);return[24,16,8,0].map(i=>s>>>i&255).join(".")}}return n}catch{return}}function mn(e){if(e==="")return"";let t="publicBaseUrl must be an HTTP(S) origin with no path, query, fragment, or credentials";if(typeof e!="string")throw new Error(t);let n;try{n=new URL(e.trim())}catch{throw new Error(t)}if(n.protocol!=="https:"&&n.protocol!=="http:"||!/^https?:\/\/[^/?#]+\/?$/i.test(e.trim())||n.pathname!=="/"||n.search||n.hash||n.username||n.password||e.includes("?")||e.includes("#"))throw new Error(t);return n.origin}function Xr(e){As=Au(e)}function ma(e){if(typeof e!="string"||e.length===0||e.length>260)return!1;let t,n,r=/^(\[[^\]]*\])(?::(\d+))?$/.exec(e);if(r){if(t=r[1],n=r[2],!Tu.test(t))return!1;try{new URL(`http://${t}/`)}catch{return!1}}else{let o=/^([^:]+)(?::(\d+))?$/.exec(e);if(!o||(t=o[1],n=o[2],t.length>253)||!Su.test(t)&&!xu.test(t))return!1}if(n!==void 0){if(!/^\d{1,5}$/.test(n))return!1;let o=Number(n);if(o<1||o>65535)return!1}return!0}function ya(e){let t=e?.trim().toLowerCase();return t==="http"||t==="https"?t:void 0}function Ru(e){let t=e.socket;return t?.encrypted?"https":t?"http":ya(e.protocol)??"http"}function xs(e){if(As)return As;let t=ku(e),n=t?fa(e,"x-forwarded-host"):void 0,r=e.get("host")?.trim(),o=ma(n)?n:ma(r)?r:void 0;if(!o)return;let s=(t?ya(fa(e,"x-forwarded-proto")):void 0)??Ru(e);try{return new URL(`${s}://${o}`).origin}catch{return}}function ar(e,t){let n=xs(e);return n?`${n}${t}`:t}function wa(e,t,n){return ar(e,vu(t,n))}function Ss(e){return e.replace(/\/mcp\/a\/[^/?#]+/gi,"/mcp/a/<redacted>")}var ks,wn,ga,As,ha,xu,Su,Tu,ve=q(()=>{"use strict";ks=require("net");ze();wn=8192,ga=1800*1e3;a(vu,"mcpAccessLinkPath");a(ku,"isTrustProxyEnabled");a(fa,"firstForwardedValue");a(Au,"normalizePublicBaseUrl");a(Dt,"canonicalizeIp");a(mn,"validateSettingsPublicBaseUrl");a(Xr,"bindPublicBaseUrl");ha="[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?",xu=new RegExp(`^${ha}(?:\\.${ha})*$`),Su=/^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/,Tu=/^\[[0-9A-Fa-f:.]+\]$/;a(ma,"isValidHostHeader");a(ya,"validScheme");a(Ru,"connectionScheme");a(xs,"requestPublicOrigin");a(ar,"absolutizePath");a(wa,"buildAccessLinkUrl");a(Ss,"redactAccessLinkTokenInPath")});function Lu(e){if(!e||e.trim().length===0)return{kind:"missing"};let t=e.trim();if(/^Basic\s+/i.test(t))return{kind:"basic"};let n=/^Bearer\s+(\S+)$/i.exec(t);return n?.[1]?{kind:"bearer",token:n[1]}:{kind:"other"}}function Ts(e,t,n,r){e.setHeader("WWW-Authenticate",fe),e.status(401).json({error:t,code:n,...r?{hint:r}:{}})}function Nu(e){let t=e??"";for(;t.endsWith("/");)t=t.slice(0,-1);return`${t}/ai-access#links`}function Fu(e,t,n,r){let o=ar(e,Nu(t));return{error:Pu,code:Cu,...n?{expiredAt:n}:{},accessLinksUrl:o,hint:r??`Ask the user to create a new access link in Node-RED on the /ai-access dashboard \u2192 Access tokens (${o}).`}}function sr(e,t,n,r,o){t.setHeader("WWW-Authenticate",fe),t.status(401).json(Fu(e,n,r,o))}function Ou(e){if(e.length<24||e.startsWith("aia_")&&e.length<40)return"The token may be truncated \u2014 ask the user for the full access-link token."}function Zr(e){return e==="get_link"?"Please open Node-RED \u2192 /ai-access \u2192 Access tokens and create a get_link access URL, then send me that /mcp/a/<token> link.":`Please open Node-RED \u2192 /ai-access \u2192 Access tokens and create a kind=${e} token, then send me the Bearer token (not a get_link path URL).`}function Mu(e,t){let n=$u;return e==="get_link"?`This URL requires a get_link token. This token is kind=${t} \u2014 ${n} for a get_link access URL (/mcp/a/<token>/\u2026). What to tell the user: ${Zr("get_link")}`:e==="rest"?`This REST endpoint requires a rest token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${Zr("rest")}`:`This MCP endpoint requires an mcp token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${Zr("mcp")}`}function ir(e,t,n){e.setHeader("WWW-Authenticate",fe),e.status(403).json({error:`Access link kind mismatch: expected ${t}, got ${n}`,code:Iu,expectedKind:t,actualKind:n,hint:Mu(t,n),tellUser:Zr(t)})}function bn(e,t,n){return(r,o,s)=>{if(!Ee(e.settings)){o.status(403).json({error:"AI Access features are disabled. Enable adminAuth, or set aiAccess.allowWithoutAdminAuth to true for local-only use.",code:"ai_features_disabled"});return}let i=Lu(r.headers.authorization);if(i.kind==="basic"){Ts(o,_u,Eu);return}if(i.kind==="missing"||i.kind==="other"){Ts(o,Ie,at);return}let d=t.validateResult(i.token);if(d.status==="expired"){sr(r,o,e.settings.httpAdminRoot,d.expiresAt);return}if(d.status!=="active"){Ts(o,$t,or,Ou(i.token));return}if(d.link.kind!==n){ir(o,n,d.link.kind);return}r.accessLink=d.link,s()}}var $t,Pu,Ie,_u,fe,at,or,Cu,Eu,Iu,$u,Mt=q(()=>{"use strict";ve();ke();$t="Access link is invalid, revoked, or no longer available (it may have expired, or Node-RED may have restarted). Ask the user for a new access link.",Pu="Access link token has expired. Ask the user for a new access link.",Ie="Access link Bearer token is missing. Ask the user for a new access link and send Authorization: Bearer <token>.",_u="Authorization must use the Bearer scheme (Authorization: Bearer <access-link-token>), not Basic. Ask the user for a new access link.",fe='Bearer realm="node-red-ai-access", charset="UTF-8"',at="access_link_missing",or="access_link_invalid",Cu="token_expired",Eu="bearer_scheme_required",Iu="wrong_token_kind";a(Lu,"parseAuthorizationHeader");a(Ts,"sendUnauthorized");a(Nu,"accessLinksPagePath");a(Fu,"tokenExpiredBody");a(sr,"sendTokenExpired");a(Ou,"truncatedBearerHint");$u="ask the owner: Node-RED /ai-access \u2192 Access tokens \u2192 create kind=rest/mcp (or get_link)";a(Zr,"whatToTellUserWrongTokenKind");a(Mu,"wrongTokenKindHint");a(ir,"sendWrongTokenKind");a(bn,"requireAccessLink")});var Q,X,Le,We,eo,Te=q(()=>{"use strict";Q=class extends Error{static{a(this,"FlowRevisionConflictError")}code="version_mismatch";statusCode=409;expectedRev;currentRev;context;constructor(t,n,r="updateFlow"){super(`Flow revision conflict in ${r}: expectedRev '${t}' does not match current rev '${n}'. The flows changed since you last read them \u2014 re-read flows, re-apply your change, and retry.`),this.name="FlowRevisionConflictError",this.expectedRev=t,this.currentRev=n,this.context=r}},X=class extends Error{static{a(this,"FlowNotFoundError")}code="flow_not_found";statusCode=404;flowId;constructor(t){super(`Flow '${t}' was not found`),this.name="FlowNotFoundError",this.flowId=t}},Le=class extends Error{static{a(this,"AdapterUnavailableError")}code="adapter_unavailable";statusCode=502;constructor(t){super(t),this.name="AdapterUnavailableError"}},We=class extends Error{static{a(this,"PluginMisconfiguredError")}code="plugin_misconfigured";statusCode=502;constructor(t){super(t),this.name="PluginMisconfiguredError"}},eo="Node-RED AI Access could not use the in-process flow runtime and the HTTP Admin API fallback returned Unauthorized. The plugin is misconfigured \u2014 tell the admin to ensure the plugin loads inside Node-RED so RED.runtime.flows is available (do not rely on unauthenticated localhost HTTP when adminAuth is enabled)."});function Ge(e,t){if(!e||!Du.test(e)||e.includes(".."))throw new Error(`Invalid ${t}`);return e}function to(e){if(!e||!Hu.test(e)||e.includes(".."))throw new Error("Invalid proposal id");return e}function Rs(e,t){let n=(0,Ht.basename)(t);if(!n||n!==t||n.includes(".."))throw new Error("Invalid file name");let r=(0,Ht.resolve)(e),o=(0,Ht.resolve)(r,n);if(o!==r&&!o.startsWith(r+Ht.sep))throw new Error("Path escapes storage directory");return o}function ba(e,t=1e5){if(typeof e=="string")return e.replace(/\0/g,"").slice(0,t)}function Ne(e){let t=e&&typeof e=="object"&&!Array.isArray(e)?e:{},n={};for(let o of ju){if(!(o in t))continue;let s=t[o];if(o==="disabled"&&typeof s=="boolean"){n[o]=s;continue}if(o==="env"&&Array.isArray(s)){let d=JSON.stringify(s);d.length<=5e5&&(n[o]=JSON.parse(d));continue}let i=ba(s,1e4);i!==void 0&&(n[o]=i)}for(let o of["nodes","configs"]){let s=t[o];if(!Array.isArray(s))continue;let i=JSON.stringify(s),d=Buffer.byteLength(i,"utf8");if(d>K)throw new ce(`flow.${o}`,d,K);n[o]=JSON.parse(i)}let r=Buffer.byteLength(JSON.stringify(n),"utf8");if(r>K)throw new ce("flow",r,K);return n}function va(e){let t={};for(let n of Uu){if(!(n in e))continue;let r=e[n];if(Bu.has(n)){if(typeof r=="number"&&Number.isFinite(r))t[n]=r;else if(typeof r=="string"&&r.trim()!==""){let s=Number(r);Number.isFinite(s)&&(t[n]=s)}continue}let o=ba(r,2e3);o!==void 0&&(t[n]=o)}return t}var Ht,Du,Hu,ju,Uu,Bu,no=q(()=>{"use strict";Ht=require("path");oe();Du=/^[A-Za-z0-9_.-]+$/,Hu=/^prop-[A-Za-z0-9_-]+$/,ju=["id","type","label","disabled","info","env"],Uu=["proposalId","withdrawalAuthority","originalOwnerId","flowId","expectedRev","resultRev","reason","code","currentRev","title","displayName","flowLabel","linkId","label","expiresAt","createdAt","ttlSeconds","kind","redactions","restoredRedactions","operation"],Bu=new Set(["ttlSeconds","redactions","restoredRedactions"]);a(Ge,"assertSafeSegment");a(to,"assertSafeProposalId");a(Rs,"resolveUnderDir");a(ba,"sanitizeString");a(Ne,"sanitizeFlowTab");a(va,"sanitizeAuditDetails")});function U(e){return e==="pending"||e==="conflict"}function so(e,t){return`oauth-${(0,oo.createHash)("sha256").update(`${e}\0${t}`).digest("hex")}`}function dt(e){return e.kind==="oauth"?so(e.clientId,e.user):e.linkId}function qu(e){return e==="pending"||e==="conflict"||e==="approved"||e==="rejected"||e==="failed"||e==="withdrawn"||e==="expired"||e==="superseded"}function Ke(e,t){return typeof e=="string"&&e.length>0?e.replace(/\0/g,"").slice(0,t):void 0}function Wu(e){if(!e||typeof e!="object"||Array.isArray(e))return;let t=e,n=Ke(t.rev,200),r=t.state;if(!n||r!=="clean"&&r!=="merged"&&r!=="conflict")return;let o=[];if(Array.isArray(t.conflicts))for(let d of t.conflicts.slice(0,200)){if(!d||typeof d!="object")continue;let l=d,c=Ke(l.kind,64),u=Ke(l.message,500);if(!c||!u)continue;let p={kind:c,message:u,choices:Array.isArray(l.choices)?l.choices.filter(g=>g==="live"||g==="proposed"):[]},f=Ke(l.nodeId,200);f&&(p.nodeId=f);let m=Ke(l.nodeType,200);m&&(p.nodeType=m);let h=Ke(l.nodeName,120);h&&(p.nodeName=h),Array.isArray(l.fields)&&(p.fields=l.fields.filter(g=>typeof g=="string"&&g.length>0).slice(0,100).map(g=>g.replace(/\0/g,"").slice(0,200))),o.push(p)}let s={rev:n,state:r,conflicts:o,liveChangedCount:typeof t.liveChangedCount=="number"&&Number.isFinite(t.liveChangedCount)?Math.max(0,Math.floor(t.liveChangedCount)):0,checkedAt:Ke(t.checkedAt,64)??""},i=Ke(t.mergedHash,64);return i&&/^[0-9a-f]+$/.test(i)&&(s.mergedHash=i),s}function Gu(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e.slice(0,200)){if(!n||typeof n!="object")continue;let{nodeId:r,property:o}=n;typeof r!="string"||typeof o!="string"||t.push({nodeId:r.replace(/\0/g,"").slice(0,200),property:o.replace(/\0/g,"").slice(0,200)})}return t.length>0?t:void 0}function Ps(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e.slice(0,zu)){if(!n||typeof n!="object"||Array.isArray(n))continue;let r=n;if(typeof r.code!="string"||!r.code||typeof r.error!="string"||!r.error)continue;let o={code:r.code.replace(/\0/g,"").slice(0,80),error:r.error.replace(/\0/g,"").slice(0,500)};typeof r.nodeId=="string"&&r.nodeId&&(o.nodeId=r.nodeId.replace(/\0/g,"").slice(0,200)),typeof r.type=="string"&&r.type&&(o.type=r.type.replace(/\0/g,"").slice(0,200)),typeof r.property=="string"&&r.property&&(o.property=r.property.replace(/\0/g,"").slice(0,200)),typeof r.configType=="string"&&r.configType&&(o.configType=r.configType.replace(/\0/g,"").slice(0,200)),Array.isArray(r.candidates)&&(o.candidates=r.candidates.filter(s=>typeof s=="string"&&s.length>0).slice(0,20).map(s=>s.replace(/\0/g,"").slice(0,200))),t.push(o)}return t.length>0?t:void 0}function ka(e){if(!Array.isArray(e))return;let t=e.filter(n=>{if(!n||typeof n!="object"||Array.isArray(n))return!1;let r=n;return typeof r.nodeId=="string"&&Array.isArray(r.fields)});return t.length?t.map(n=>({nodeId:n.nodeId.replace(/\0/g,"").slice(0,200),fields:n.fields.filter(r=>typeof r=="string").map(r=>r.replace(/\0/g,"").slice(0,500))})):void 0}var oo,yt,Aa,me,Y,wt,Re,zu,Je,ro,Ve=q(()=>{"use strict";oo=require("crypto"),yt=require("fs"),Aa=require("path");no();oe();a(U,"isOpenProposalStatus");a(so,"oauthOwnerId");a(dt,"proposalOwnerId");me=class extends Error{static{a(this,"ProposalNotFoundError")}code="proposal_not_found";statusCode=404;constructor(t){super(`Proposal '${t}' was not found`),this.name="ProposalNotFoundError"}},Y=class extends Error{static{a(this,"ProposalNotPendingError")}code="proposal_not_pending";statusCode=409;constructor(t,n){super(`Proposal '${t}' is '${n}', not pending`),this.name="ProposalNotPendingError"}},wt=class extends Error{static{a(this,"ProposalNotOwnedError")}code="proposal_not_owned";statusCode=403;constructor(t){super(`Proposal '${t}' was not created by this access link`),this.name="ProposalNotOwnedError"}},Re=class extends Error{static{a(this,"ProposalConcurrentApproveError")}code="proposal_approve_in_progress";statusCode=409;constructor(t){super(`Proposal '${t}' is already being approved`),this.name="ProposalConcurrentApproveError"}};a(qu,"isProposalStatus");zu=50;a(Ke,"cleanText");a(Wu,"sanitizeRebaseInfo");Je=class extends Error{static{a(this,"ProposalConflictError")}code="proposal_conflict";statusCode=409;conflicts;constructor(t,n){super(`Proposal '${t}' conflicts with changes made in the editor since it was submitted; resolve the conflicts before approving.`),this.name="ProposalConflictError",this.conflicts=n}};a(Gu,"sanitizeRestoredSecrets");a(Ps,"sanitizeProposalWarnings");a(ka,"sanitizeAiOverrides");ro=class{constructor(t,n={}){this.paths=t;this.now=n.now??(()=>Date.now()),this.randomId=n.randomId??(()=>(0,oo.randomBytes)(12).toString("hex")),this.maxProposalBytes=n.maxProposalBytes??K}static{a(this,"ProposalStore")}inFlight=new Set;now;randomId;maxProposalBytes;fileFor(t){let n;try{n=to(t)}catch{throw new me(t)}return Rs(this.paths.proposals,`${n}.json`)}toDiskRecord(t){let n=to(t.id),r=Ge(String(t.expectedRev).slice(0,200),"expectedRev"),o=Ge(String(t.payload.flowId).slice(0,200),"flowId"),s=Ne(t.payload.flow),i=t.baseFlow!==void 0?Ne(t.baseFlow):void 0,d=t.createdBy.kind==="oauth"?{kind:"oauth",clientId:Ge(String(t.createdBy.clientId).slice(0,200),"clientId"),user:String(t.createdBy.user).replace(/\0/g,"").slice(0,200)}:{kind:"access-link",linkId:Ge(String(t.createdBy.linkId).slice(0,200),"linkId"),...typeof t.createdBy.label=="string"?{label:t.createdBy.label.replace(/\0/g,"").slice(0,200)}:{}},l={id:n,type:"replace_flow",status:qu(t.status)?t.status:"pending",expectedRev:r,payload:{flowId:o,flow:s},createdBy:d,createdAt:String(t.createdAt).slice(0,64),updatedAt:String(t.updatedAt).slice(0,64)};i!==void 0&&(l.baseFlow=i),typeof t.title=="string"&&t.title.length>0&&(l.title=t.title.replace(/\0/g,"").slice(0,80).trim(),l.title||delete l.title);let c=Ps(t.warnings);c&&(l.warnings=c);let u=Gu(t.restoredSecrets);if(u&&(l.restoredSecrets=u),t.deployPending&&U(l.status)){let h=Ke(t.deployPending.mergedHash,64),g=Ke(t.deployPending.reason,500);h&&/^[0-9a-f]+$/.test(h)&&g&&(l.deployPending={mergedHash:h,reason:g},t.deployPending.queueReleased===!0&&(l.deployPending.queueReleased=!0))}let p=ka(t.aiConflictOverrides);p&&U(l.status)&&(l.aiConflictOverrides=p);let f=t.conflictChoice;f&&U(l.status)&&(f.choice==="live"||f.choice==="proposed")&&typeof f.nodeId=="string"&&f.undo&&(l.conflictChoice={nodeId:f.nodeId.replace(/\0/g,"").slice(0,200),choice:f.choice,...f.chooser==="ai"||f.chooser==="human"?{chooser:f.chooser}:{},undo:{baseFlow:Ne(f.undo.baseFlow),flow:Ne(f.undo.flow),warnings:Ps(f.undo.warnings)??[],aiConflictOverrides:ka(f.undo.aiConflictOverrides)??[]}});let m=Wu(t.rebase);if((m&&U(l.status)||m&&m.state!=="conflict")&&(l.rebase=m),t.decidedBy)if(t.decidedBy.kind==="access-link"){let h={kind:"access-link",linkId:Ge(String(t.decidedBy.linkId).slice(0,200),"linkId")};typeof t.decidedBy.label=="string"&&(h.label=t.decidedBy.label.replace(/\0/g,"").slice(0,200)),l.decidedBy=h}else{let h={kind:"admin"};typeof t.decidedBy.username=="string"&&(h.username=t.decidedBy.username.replace(/\0/g,"").slice(0,200)),l.decidedBy=h}return typeof t.reason=="string"&&(l.reason=t.reason.replace(/\0/g,"").slice(0,2e3)),typeof t.resultRev=="string"&&(l.resultRev=Ge(t.resultRev.slice(0,200),"resultRev")),l}writeAtomic(t,n){let r=this.fileFor(t),o=this.toDiskRecord(n),s=ht(o,this.maxProposalBytes,{kind:"proposal",pretty:!0});un(this.paths.root,r,s)}create(t){let n=new Date(this.now()).toISOString(),r=to(`prop-${this.randomId()}`),o={id:r,type:"replace_flow",status:"pending",expectedRev:t.expectedRev,payload:{flowId:t.payload.flowId,flow:t.payload.flow},baseFlow:t.baseFlow,title:t.title,createdBy:t.createdBy,createdAt:n,updatedAt:n},s=Ps(t.warnings);return s&&(o.warnings=s),t.restoredSecrets?.length&&(o.restoredSecrets=t.restoredSecrets),this.writeAtomic(r,o),this.get(r)}get(t){let n=this.fileFor(t);if(!(0,yt.existsSync)(n))throw new me(t);return JSON.parse((0,yt.readFileSync)(n,"utf8"))}list(t){if(!(0,yt.existsSync)(this.paths.proposals))return[];let n=(0,yt.readdirSync)(this.paths.proposals).filter(o=>o.endsWith(".json")),r=[];for(let o of n){let s=(0,Aa.basename)(o);if(!(s!==o||s.includes("..")))try{let i=JSON.parse((0,yt.readFileSync)(Rs(this.paths.proposals,s),"utf8"));if(t?.linkId&&dt(i.createdBy)!==t.linkId||t?.status&&i.status!==t.status||t?.flowId&&i.payload.flowId!==t.flowId)continue;r.push(i)}catch{}}return r.sort((o,s)=>o.createdAt<s.createdAt?1:-1)}beginApprove(t){if(this.inFlight.has(t))throw new Re(t);let n=this.get(t);if(!U(n.status))throw new Y(t,n.status);return this.inFlight.add(t),n}releaseLock(t){this.inFlight.delete(t)}recordDeployPending(t,n){let r=this.get(t);if(!U(r.status))throw new Y(t,r.status);return n?r.deployPending=n:delete r.deployPending,r.status="pending",r.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,r),this.get(t)}markApproved(t,n,r){let o=this.get(t);return o.status="approved",delete o.deployPending,o.decidedBy=n,o.resultRev=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markRejected(t,n,r){if(this.inFlight.has(t))throw new Re(t);let o=this.get(t);return o.status="rejected",n&&(o.decidedBy=n),o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markFailed(t,n,r){let o=this.get(t);return o.status="failed",n&&(o.decidedBy=n),o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markWithdrawn(t,n,r){if(this.inFlight.has(t))throw new Re(t);let o=this.get(t);if(!U(o.status))throw new Y(t,o.status);return o.status="withdrawn",o.decidedBy=n,o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markClosed(t,n,r,o){let s=this.get(t);if(!U(s.status))throw new Y(t,s.status);return s.status=n,o&&(s.decidedBy=o),s.reason=r,s.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,s),this.get(t)}recordRebase(t,n){let r=this.get(t);if(!U(r.status))return r;let o=n.state==="conflict"?"conflict":"pending",s=r.rebase;return r.status===o&&s!==void 0&&s.rev===n.rev&&s.state===n.state&&s.mergedHash===n.mergedHash&&s.liveChangedCount===n.liveChangedCount&&JSON.stringify(s.conflicts)===JSON.stringify(n.conflicts)?r:(r.status=o,r.rebase=n,r.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,r),this.get(t))}updateBody(t,n){let r=this.get(t);if(!U(r.status))throw new Y(t,r.status);if(this.inFlight.has(t))throw new Re(t);return r.baseFlow=n.baseFlow,r.payload={flowId:r.payload.flowId,flow:n.flow},n.expectedRev!==void 0&&(r.expectedRev=n.expectedRev),n.title!==void 0&&(r.title=n.title),n.restoredSecrets!==void 0&&(n.restoredSecrets.length>0?r.restoredSecrets=n.restoredSecrets:delete r.restoredSecrets),n.warnings!==void 0&&(n.warnings.length>0?r.warnings=n.warnings:delete r.warnings),n.aiConflictOverrides?.length?r.aiConflictOverrides=n.aiConflictOverrides:delete r.aiConflictOverrides,n.conflictChoice?r.conflictChoice=n.conflictChoice:delete r.conflictChoice,r.status="pending",delete r.rebase,r.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,r),this.get(t)}isApproving(t){return this.inFlight.has(t)}}});function xa(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new Error(`${t} must be a node object`);let n=e;if(typeof n.id!="string"||n.id.length===0)throw new Error(`${t} needs a non-empty string id`);if(typeof n.type!="string"||n.type.length===0)throw new Error(`${t} '${n.id}' needs a non-empty string type`);return n}function io(e,t,n){let r=e.find(d=>d.id===t);if(!r||r.type!=="tab"&&r.type!=="subflow")throw new X(t);let o={...r,id:t,type:r.type};delete o.nodes,delete o.configs,delete o.subflows,n.label!==void 0&&(o.label=n.label),n.disabled!==void 0&&(o.disabled=n.disabled),n.info!==void 0&&(o.info=n.info),n.env!==void 0&&(o.env=n.env);let s=[];for(let[d,l]of(n.nodes??[]).entries())s.push({...xa(l,`flowData.nodes[${d}]`),z:t});for(let[d,l]of(n.configs??[]).entries())s.push({...xa(l,`flowData.configs[${d}]`),z:t});return[...e.filter(d=>d.id!==t&&d.z!==t),o,...s]}function dr(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new Error(`Expected ${t} to be an object with rev and flows`);let n=e;if(typeof n.rev!="string"||n.rev.length===0)throw new Error(`Expected ${t}.rev to be a non-empty string`);if(!Array.isArray(n.flows))throw new Error(`Expected ${t}.flows to be an array`);return{rev:n.rev,flows:n.flows}}function vn(e,t){if(Array.isArray(t)){let d=t.find(u=>u&&typeof u=="object"&&u.id===e&&(u.type==="tab"||u.type==="subflow"));if(!d)throw new X(e);let l=t.filter(u=>u&&typeof u=="object"&&u.id!==e&&u.z===e),c={id:e,type:d.type,nodes:l};return typeof d.label=="string"&&(c.label=d.label),typeof d.disabled=="boolean"&&(c.disabled=d.disabled),typeof d.info=="string"&&(c.info=d.info),c}if(!t||typeof t!="object")throw new X(e);let n=t,r=typeof n.id=="string"&&n.id.length>0?n.id:e,o=typeof n.type=="string"&&n.type.length>0?n.type:"tab",s=Array.isArray(n.nodes)?n.nodes:[],i={...n,id:r,type:o,nodes:s};return Array.isArray(n.configs)&&(i.configs=n.configs),i}var ao=q(()=>{"use strict";Te();a(xa,"asFlowNode");a(io,"replaceFlowTabInFlatFlows");a(dr,"assertFlowsSnapshot");a(vn,"normalizeFlowTab")});function Sa(e,t=new _s){return lo in e?e:{kind:e.kind,[lo]:t,async getFlows(){let r=await e.getFlows();return t.record(r),r},async getFlow(r){let o=await e.getFlow(r);if(!t.has(o.rev))try{t.record(await e.getFlows())}catch{}return o},async updateFlow(r){return e.updateFlow(r)}}}function Cs(e){return lo in e?e[lo]:void 0}function co(e,t){return{kind:t,async getFlows(){return{rev:e.rev,flows:JSON.parse(JSON.stringify(e.flows))}},async getFlow(n){let r=JSON.parse(JSON.stringify(e.flows)),o=r.find(l=>l.id===n&&(l.type==="tab"||l.type==="subflow"));if(!o)throw new X(n);let s=[],i=[];for(let l of r){if(l.id===n||l.z!==n)continue;(l.type!=="group"&&l.x===void 0&&l.y===void 0&&l.wires===void 0?i:s).push(l)}let d={id:n,type:o.type,nodes:s};for(let l of["label","disabled","info","env"])o[l]!==void 0&&(d[l]=o[l]);return i.length>0&&(d.configs=i),{rev:e.rev,flow:d}},async updateFlow(){throw new Error("snapshot adapter is read-only")}}}var Ku,_s,lo,Es=q(()=>{"use strict";Te();Ku=24,_s=class{constructor(t=Ku){this.maxRevs=t}static{a(this,"RevHistory")}byRev=new Map;record(t){if(!(!t||typeof t.rev!="string"||!Array.isArray(t.flows))&&!this.byRev.has(t.rev))for(this.byRev.set(t.rev,{rev:t.rev,flows:JSON.parse(JSON.stringify(t.flows))});this.byRev.size>this.maxRevs;){let n=this.byRev.keys().next().value;if(n===void 0)break;this.byRev.delete(n)}}get(t){let n=this.byRev.get(t);return n?{rev:n.rev,flows:JSON.parse(JSON.stringify(n.flows))}:void 0}has(t){return this.byRev.has(t)}},lo=Symbol.for("ai-access.revHistory");a(Sa,"withRevHistory");a(Cs,"revHistoryOf");a(co,"snapshotAdapter")});function Fs(e){return e?Ju.has(e)?!0:e.startsWith("subflow:"):!1}function bt(e,t="node.type"){if(e==null||e==="")throw new k(`${t} is required`,"missing_node_type");if(typeof e!="string")throw new k(`${t} must be a non-empty string (got ${typeof e})`,"invalid_node_type",{details:{type:e,got:typeof e}});if(!e.trim())throw new k(`${t} is required`,"missing_node_type");return e}function tp(e,t){if(typeof e!="string"||typeof t!="string")return Number.MAX_SAFE_INTEGER;let n=e.toLowerCase(),r=t.toLowerCase();if(n===r)return 0;let o=n.length,s=r.length;if(o===0)return s;if(s===0)return o;let i=new Array(s+1),d=new Array(s+1);for(let l=0;l<=s;l++)i[l]=l;for(let l=1;l<=o;l++){d[0]=l;let c=n.charAt(l-1);for(let u=1;u<=s;u++){let p=c===r.charAt(u-1)?0:1;d[u]=Math.min((d[u-1]??0)+1,(i[u]??0)+1,(i[u-1]??0)+p)}for(let u=0;u<=s;u++)i[u]=d[u]??0}return i[s]??s}function np(e,t){let n=e.toLowerCase(),r=t.toLowerCase(),o=Math.min(n.length,r.length),s=0;for(;s<o&&n.charAt(s)===r.charAt(s);)s+=1;return s}function op(e){return Math.max(2,Math.floor(e.length/3))}function Os(e,t,n=5){if(typeof e!="string"||!e)return[];let r=op(e);return t.filter(s=>typeof s=="string"&&s!==e&&!rp.has(s)&&!s.startsWith("_")&&!s.startsWith("subflow:")).map(s=>({c:s,dist:tp(e,s),prefix:np(e,s)})).filter(s=>s.dist<=r).sort((s,i)=>s.dist-i.dist||i.prefix-s.prefix||s.c.localeCompare(i.c)).slice(0,n).map(s=>s.c)}function sp(e){return e.endsWith("/")?e.slice(0,-1):e}function Ia(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Ea.createRequire)(e)}function ip(){try{let t=Ia()("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}function La(e){let t=e.types,n=Array.isArray(t)?t.filter(i=>typeof i=="string"&&i.length>0):[],r="";if(typeof e.module=="string"&&e.module)r=e.module;else if(typeof e.name=="string"&&e.name)r=e.name;else if(typeof e.id=="string"){let i=String(e.id).split("/")[0];r=i&&i.length>0?i:String(e.id)}let o=typeof e.id=="string"&&e.id?e.id:n.length===1&&r?`${r}/${n[0]}`:r;if(!o||n.length===0)return null;let s={id:o,module:r.length>0?r:o,types:n};return typeof e.version=="string"&&(s.version=e.version),typeof e.enabled=="boolean"&&(s.enabled=e.enabled),typeof e.name=="string"&&(s.name=e.name),s}function $s(e=ep,t={}){let n=t.module??"node-red",r=t.version??"4.0.0",o=[...e],s=t.configs??{};return{listNodeSets(){return Promise.resolve([{id:n,module:n,version:r,types:o,enabled:!0,name:n}])},getNodeConfigHtml(i){if(s[i])return Promise.resolve(s[i]??null);let d=i.includes("/")?i.slice(i.lastIndexOf("/")+1):i;return s[d]?Promise.resolve(s[d]??null):Promise.resolve(null)}}}function Na(e={}){if(ip())try{let n=Ia()("@node-red/runtime");if(n.nodes&&typeof n.nodes.getNodeList=="function")return new Ns(n.nodes)}catch{}if(e.baseUrl)return new Ls({baseUrl:e.baseUrl,...e.accessToken?{accessToken:e.accessToken}:{},...e.fetch?{fetch:e.fetch}:{}});throw new Error("No usable NodeTypesAdapter: runtime.nodes unavailable and no HTTP baseUrl")}async function Ms(e){let t=await e.listNodeSets(),n=new Map;for(let r of t)for(let o of r.types){if(n.has(o))continue;let s={type:o,module:r.module,setId:r.id};typeof r.enabled=="boolean"&&(s.enabled=r.enabled),r.version&&(s.version=r.version),n.set(o,s)}return{byType:n,allNames:[...n.keys()]}}function ap(e,t){let n=t.toLowerCase();return[e.type,e.module,e.category??"",e.description??""].join(`
4
+ `).toLowerCase().includes(n)}async function po(e,t={}){let n=t.limit??Vu,{byType:r}=await Ms(e),o=[...r.values()].sort((u,p)=>u.type.localeCompare(p.type)),s=t.q?.trim();s&&(o=o.filter(u=>ap(u,s)));let i=o.length,d=i>n,c={types:o.slice(0,n),total:i,truncated:d};return s&&(c.q=s),c}function dp(e,t){let n=`data-help-name="${t}"`,r=`data-help-name='${t}'`,o=e.toLowerCase(),s=0;for(;s<e.length;){let i=e.indexOf(n,s),d=e.indexOf(r,s),l=-1;if(i>=0&&(d<0||i<d)?l=i:d>=0&&(l=d),l<0)return null;let c=o.lastIndexOf("<script",l);if(c<0||c<s){s=l+1;continue}let u=e.indexOf(">",l);if(u<0)return null;let p=o.indexOf("</script",u+1);if(p<0)return null;let f=p+8;for(;f<e.length&&/\s/.test(e.charAt(f));)f+=1;if(e.charAt(f)!==">"){s=p+1;continue}return e.slice(u+1,p)}return null}function Ra(e,t){let n=`<${t}`,r=`</${t}`,o="",s=0,i=e.toLowerCase();for(;s<e.length;){let d=i.indexOf(n,s);if(d<0){o+=e.slice(s);break}o+=e.slice(s,d);let l=e.indexOf(">",d);if(l<0)break;let c=i.indexOf(r,l+1);if(c<0)break;let u=c+r.length;for(;u<e.length&&/\s/.test(e.charAt(u));)u+=1;e.charAt(u)===">"?(s=u+1,o+=" "):s=c+r.length}return o}function lp(e,t=2e3){let n=Ra(e,"script");return n=Ra(n,"style"),n=n.replace(/<[^>]+>/g," ").replace(/&nbsp;/gi," ").replace(/[#*_`]+/g," ").replace(/\s+/g," ").trim(),n.length>t&&(n=`${n.slice(0,t-1)}\u2026`),n}function cp(e,t){try{let n=/RED\.nodes\.registerType\s*\(\s*['"]([^'"]+)['"]\s*,\s*\{/.exec(e);if(!n?.[1])return null;let r=n[1];if(t&&r!==t){let o=/RED\.nodes\.registerType\s*\(\s*['"]([^'"]+)['"]\s*,\s*\{/g,s,i=null,d=-1;for(;(s=o.exec(e))!==null;)if(s[1]===t){i=s[1],d=s.index;break}return!i||d<0?null:Pa(e,d,i)}return Pa(e,n.index,r)}catch{return null}}function Pa(e,t,n){let r=e.slice(t),o=r.indexOf("{");if(o<0)return{type:n};let s=lr(r.slice(o));if(!s)return{type:n};let i=_a(s,"category"),d=_a(s,"color"),l=Ca(s,"inputs"),c=Ca(s,"outputs"),u=pp(s),p=[];if(u){for(let g of Object.values(u))if(g&&typeof g=="object"&&!Array.isArray(g)){let y=g.type;typeof y=="string"&&y.length>0&&p.push(y)}}let f,m=dp(e,n);m&&(f=lp(m));let h={type:n};return i&&(h.category=i),d&&(h.color=d),l!==void 0&&(h.inputs=l),c!==void 0&&(h.outputs=c),u&&(h.defaults=u),p.length>0&&(h.requiredConfigNodeTypes=[...new Set(p)]),f&&(h.help=f),h}function up(e){let t=e.trim();if(!t)return{};if(!/\n(?=#{1,3}\s*(inputs?|outputs?)\b)/i.test(`
5
+ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(let l of r){let c=l.trim();if(!c)continue;let u=/^(#{1,3})\s*(inputs?|outputs?)\b[\s:]*\n?([\s\S]*)$/i.exec(c);if(u){let p=(u[2]??"").toLowerCase(),f=(u[3]??"").trim();p.startsWith("input")?s=f||c:i=f||c}else o||(o=c.replace(/^#{1,3}\s*/,"").trim())}let d={};return o&&(d.summary=o),s&&(d.inputs=s),i&&(d.outputs=i),!d.summary&&!d.inputs&&!d.outputs&&(d.text=t),d}function lr(e){if(!e.startsWith("{"))return null;let t=0,n=null,r=!1;for(let o=0;o<e.length;o++){let s=e[o];if(n){if(r){r=!1;continue}if(s==="\\"){r=!0;continue}s===n&&(n=null);continue}if(s==='"'||s==="'"||s==="`"){n=s;continue}if(s==="{")t+=1;else if(s==="}"&&(t-=1,t===0))return e.slice(0,o+1)}return null}function _a(e,t){let n=new RegExp(`\\b${t}\\s*:\\s*['"]([^'"]+)['"]`);return e.match(n)?.[1]}function Ca(e,t){let n=new RegExp(`\\b${t}\\s*:\\s*(-?\\d+(?:\\.\\d+)?)`),r=e.match(n);if(!r?.[1])return;let o=Number(r[1]);return Number.isFinite(o)?o:void 0}function pp(e){let t=e.search(/\bdefaults\s*:/);if(t<0)return;let n=e.slice(t),r=n.indexOf("{");if(r<0)return;let o=lr(n.slice(r));if(!o)return;let s={},i=/([a-zA-Z_$][\w$]*)\s*:\s*\{/g,d;for(;(d=i.exec(o))!==null;){let l=d[1];if(!l||l==="defaults")continue;let c=lr(o.slice(d.index+d[0].length-1));if(!c)continue;let u={},p=/\btype\s*:\s*['"]([^'"]+)['"]/.exec(c);p?.[1]&&(u.type=p[1]),/\brequired\s*:\s*true\b/.test(c)&&(u.required=!0);let f=/\bvalue\s*:\s*/.exec(c);if(f){let m=c.slice(f.index+f[0].length).trimStart();u.value=mp(m)}s[l]=u}return Object.keys(s).length>0?s:void 0}function fp(e,t){let n=e.charAt(t);if(n==="u"&&/^[0-9a-fA-F]{4}/.test(e.slice(t+1,t+5)))return{char:String.fromCharCode(Number.parseInt(e.slice(t+1,t+5),16)),next:t+5};if(n==="x"&&/^[0-9a-fA-F]{2}/.test(e.slice(t+1,t+3)))return{char:String.fromCharCode(Number.parseInt(e.slice(t+1,t+3),16)),next:t+3};let r;switch(n){case"n":r=`
6
+ `;break;case"r":r="\r";break;case"t":r=" ";break;case"b":r="\b";break;case"f":r="\f";break;case"v":r="\v";break;case"0":r="\0";break;case"\\":r="\\";break;case"'":r="'";break;case'"':r='"';break;case"/":r="/";break;default:r=n;break}return{char:r,next:t+1}}function hp(e){return e==="_DEFAULT_"?"":e}function uo(e){let t=e.trimStart();if(t.startsWith("'")||t.startsWith('"')){let r=t[0],o=1,s="";for(;o<t.length;){let i=t[o];if(i==="\\"&&o+1<t.length){let d=fp(t,o+1);s+=d.char,o=d.next;continue}if(i===r)break;s+=i,o+=1}return hp(s)}if(t.startsWith("[")){let r=Fa(t);if(!r)return;let o=r.slice(1,-1).trim();if(!o)return[];let s=[],i=0;for(;i<o.length;){for(;i<o.length&&/[\s,]/.test(o.charAt(i));)i+=1;if(i>=o.length)break;let d=o.slice(i),l=uo(d);s.push(l);let c=Is(d);i+=c>0?c:1}return s}if(t.startsWith("{")){let r=lr(t);if(!r)return;let o=r.slice(1,-1);if(!o.trim())return{};let s={},i=0;for(;i<o.length;){for(;i<o.length&&/[\s,]/.test(o.charAt(i));)i+=1;if(i>=o.length)break;let d;if(o[i]==="'"||o[i]==='"'){let c=uo(o.slice(i));d=typeof c=="string"?c:"",i+=Is(o.slice(i))}else{let c=/^[a-zA-Z_$][\w$]*/.exec(o.slice(i));if(!c)break;d=c[0],i+=d.length}for(;i<o.length&&/\s/.test(o.charAt(i));)i+=1;if(o[i]!==":")break;for(i+=1;i<o.length&&/\s/.test(o.charAt(i));)i+=1;let l=o.slice(i);s[d]=uo(l),i+=Is(l)}return s}let n=/^-?\d+(?:\.\d+)?/.exec(t);if(n)return Number(n[0]);if(t.startsWith("true"))return!0;if(t.startsWith("false"))return!1;if(t.startsWith("null"))return null}function Is(e){let t=e.trimStart(),n=e.length-t.length;if(t.startsWith("'")||t.startsWith('"')){let o=t[0],s=1;for(;s<t.length;){if(t[s]==="\\"&&s+1<t.length){s+=2;continue}if(t[s]===o)return n+s+1;s+=1}return n+t.length}if(t.startsWith("[")){let o=Fa(t);return n+(o?.length??1)}if(t.startsWith("{")){let o=lr(t);return n+(o?.length??1)}let r=/^(?:-?\d+(?:\.\d+)?|true|false|null)/.exec(t);return r?n+r[0].length:n+1}function Fa(e){if(!e.startsWith("["))return null;let t=0,n=null,r=!1;for(let o=0;o<e.length;o++){let s=e[o];if(n){if(r){r=!1;continue}if(s==="\\"){r=!0;continue}s===n&&(n=null);continue}if(s==="'"||s==='"'){n=s;continue}if(s==="[")t+=1;else if(s==="]"&&(t-=1,t===0))return e.slice(0,o+1)}return null}function mp(e){return uo(e)}function gp(e,t){for(let n of e)if(n.types.includes(t)){let r=n.id.includes("/")||n.types.length===1?n.id:`${n.module}/${t}`;return{set:n,setId:r}}return null}function Ds(e,t){let n=e.toLowerCase();return t.includes(e)?e:t.find(r=>r.toLowerCase()===n)}async function jt(e,t,n={}){if(!t||typeof t!="string")throw new k("Missing node type","missing_node_type");let r=n.includeHelp!==!1;if(Fs(t)){let l={type:t,module:"node-red",category:"config",inputs:0,outputs:0,defaults:null};return r&&(l.help={summary:"Built-in structural Node-RED type (not a palette node)."}),l}let o=await e.listNodeSets(),s=o.flatMap(l=>l.types),i=gp(o,t);if(!i){let l=Ds(t,s);throw l&&l!==t?new k(`Node type '${t}' was not found \u2014 did you mean '${l}'? Types are case-sensitive.`,"node_type_not_found",{statusCode:404,details:{type:t,didYouMean:l,caseSensitive:!0,nearest:[l]}}):new k(`Node type '${t}' was not found`,"node_type_not_found",{statusCode:404,details:{type:t,nearest:Os(t,s,5)}})}let d={type:t,module:i.set.module,setId:i.setId,inputs:1,outputs:1,defaults:null};if(i.set.version&&(d.version=i.set.version),typeof i.set.enabled=="boolean"&&(d.enabled=i.set.enabled),typeof e.getNodeConfigHtml=="function")try{let l=await e.getNodeConfigHtml(i.setId)??await e.getNodeConfigHtml(`${i.set.module}/${t}`)??await e.getNodeConfigHtml(t);if(l){let c=cp(l,t);c&&(c.category&&(d.category=c.category),c.color&&(d.color=c.color),d.inputs=c.inputs??d.inputs,d.outputs=c.outputs??d.outputs,c.defaults&&Object.keys(c.defaults).length>0?d.defaults=c.defaults:d.defaults=null,c.requiredConfigNodeTypes&&(d.requiredConfigNodeTypes=c.requiredConfigNodeTypes),r&&c.help&&(d.help=up(c.help)))}}catch{}return d}async function Oa(e,t){let{allNames:n}=await Ms(e),r=new Set(n),o=new Set,s=a(l=>{let c=bt(l,"node.type");if(o.has(c)||Fs(c)){o.add(c);return}if(o.add(c),r.has(c))return;let u=Ds(c,n);throw u&&u!==c?new k(`Unknown node type '${c}' \u2014 did you mean '${u}'? Types are case-sensitive.`,"unknown_node_type",{details:{type:c,didYouMean:u,caseSensitive:!0,nearest:[u]}}):new k(`Unknown node type '${c}'. Call node-types / palette search; ask the user to install via Manage palette, then recheck.`,"unknown_node_type",{details:{type:c,nearest:Os(c,n,5)}})},"checkType"),i=a(l=>{if(!l||typeof l!="object"||Array.isArray(l))return;let c=l;typeof c.id=="string"&&c.id.length>0&&s(c.type)},"visitNodeOrConfig");a(l=>{if(!l||typeof l!="object"||Array.isArray(l))return;let c=l;if(c.type!==void 0&&s(c.type),Array.isArray(c.nodes))for(let u of c.nodes)i(u);if(Array.isArray(c.configs))for(let u of c.configs)i(u)},"visitTab")(t)}async function $a(e,t){let n=bt(t,"node.type");if(Fs(n))return;let{allNames:r}=await Ms(e);if(r.includes(n))return;let o=Ds(n,r);throw o&&o!==n?new k(`Unknown node type '${n}' \u2014 did you mean '${o}'? Types are case-sensitive.`,"unknown_node_type",{details:{type:n,didYouMean:o,caseSensitive:!0,nearest:[o]}}):new k(`Unknown node type '${n}'. Call node-types / palette search; ask the user to install via Manage palette, then recheck.`,"unknown_node_type",{details:{type:n,nearest:Os(n,r,5)}})}async function yp(e,t){let n=new Headers({Accept:"application/json"});t&&n.set("If-None-Match",t);let r=await fetch(e,{headers:n}),o=r.headers.get("etag")??void 0;if(r.status===304)return{status:304,...o?{etag:o}:{}};if(!r.ok)return{status:r.status,body:null,...o?{etag:o}:{}};let s=await r.json();return{status:r.status,body:s,...o?{etag:o}:{}}}async function wp(e,t){let n=Date.now();if(Fe&&n-Fe.fetchedAt<Qu)return Fe.body;let r=Fe?.etag,o=await e(t,r);if(o.status===304&&Fe)return Fe={...Fe,fetchedAt:n,...o.etag?{etag:o.etag}:{}},Fe.body;if(!o.body||!Array.isArray(o.body.modules)){if(Fe)return Fe.body;throw new k("Node-RED catalogue unavailable","catalogue_unavailable",{statusCode:502})}return Fe={fetchedAt:n,body:o.body,...o.etag?{etag:o.etag}:{}},o.body}async function fo(e,t,n={}){let r=t.trim();if(!r)throw new k("q query parameter is required","missing_query");let o=n.limit??Yu,s=n.catalogueUrl??Zu,i=n.catalogueFetcher??yp,d=await wp(i,s),l=Array.isArray(d.modules)?d.modules:[],c=await e.listNodeSets(),u=new Set(c.flatMap(g=>{let y=[g.module,g.id];return g.id.includes("/")&&y.push(g.id.split("/")[0]??g.module),y.map(w=>w.toLowerCase())})),p=r.toLowerCase(),f=[];for(let g of l){let y=g.id.toLowerCase(),w=(g.keywords??[]).map(I=>String(I).toLowerCase()),v=(g.description??"").toLowerCase(),L=bp(p,y,w,v);if(L<=0)continue;let P={module:g.id,installed:u.has(y)};typeof g.description=="string"&&(P.description=g.description),typeof g.version=="string"&&(P.version=g.version),typeof g.updated_at=="string"&&(P.updated=g.updated_at),typeof g.downloads=="number"&&(P.downloads=g.downloads),typeof g.url=="string"&&(P.link=g.url),f.push({item:P,score:L})}f.sort((g,y)=>y.score-g.score||(y.item.downloads??0)-(g.item.downloads??0)||g.item.module.localeCompare(y.item.module));let m=f.length,h=m>o;return{q:r,modules:f.slice(0,o).map(g=>g.item),total:m,truncated:h,hint:Xu}}function bp(e,t,n,r){if(!e)return 0;if(t===e||t===`node-red-contrib-${e}`||t===`node-red-node-${e}`)return 1e4;let o=0;t.startsWith(`node-red-contrib-${e}`)||t.startsWith(`node-red-node-${e}`)?o+=500:t.startsWith(e)?o+=80:t.includes(e)&&(o+=60);for(let s of n)s===e?o+=50:s.startsWith(e)||e.startsWith(s)?o+=35:s.includes(e)&&(o+=25);return r.includes(e)&&(o+=10),o}var Ea,Ju,Vu,Yu,Xu,Zu,Qu,ep,rp,Ls,Ta,Ns,Fe,Ut=q(()=>{"use strict";Ea=require("module");se();Ju=new Set(["tab","subflow","group","junction"]),Vu=100,Yu=30,Xu="Recommend only \u2014 ask the user to install via Manage palette, then recheck node-types. This API never installs. Core Node-RED nodes (module node-red) are not listed in the public catalogue; use node-types for installed core types.",Zu="https://catalogue.nodered.org/catalogue.json",Qu=3600*1e3,ep=["inject","debug","function","template","comment","change","switch","delay","trigger","link in","link out","link call","mqtt in","mqtt out","http in","http response","http request","websocket in","websocket out","tcp in","tcp out","udp in","udp out","catch","status","complete","join","split","sort","batch","csv","html","json","xml","yaml","file","file in","watch","exec","range","rbe"];a(Fs,"isStructuralNodeType");a(bt,"requireNodeTypeName");a(tp,"editDistance");a(np,"sharedPrefixLength");rp=new Set(["unknown","tab","subflow","group"]);a(op,"nearestDistanceCutoff");a(Os,"nearestNodeTypes");a(sp,"trimTrailingSlash");a(Ia,"createPluginRequire");a(ip,"isRuntimeSettingsAvailable");a(La,"normalizeNodeSet");a($s,"createStaticNodeTypesAdapter");Ls=class{static{a(this,"HttpNodeTypesAdapter")}baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=sp(t.baseUrl),this.accessToken=t.accessToken,this.fetchImpl=t.fetch??globalThis.fetch.bind(globalThis)}headers(t){let n=new Headers;return n.set("Accept",t),this.accessToken&&n.set("Authorization",`Bearer ${this.accessToken}`),n}async listNodeSets(){let t=await this.fetchImpl(`${this.baseUrl}/nodes`,{headers:this.headers("application/json")});if(!t.ok)throw new Error(`Admin API GET /nodes failed (${t.status})`);let n=await t.json();if(!Array.isArray(n))throw new Error("Admin API GET /nodes returned non-array JSON");let r=[];for(let o of n){if(!o||typeof o!="object"||Array.isArray(o))continue;let s=La(o);s&&r.push(s)}return r}async getNodeConfigHtml(t,n){let r=t.split("/").map(i=>encodeURIComponent(i)).join("/"),o=n?`?lang=${encodeURIComponent(n)}`:"",s=await this.fetchImpl(`${this.baseUrl}/nodes/${r}${o}`,{headers:this.headers("text/html")});return s.status===404||!s.ok?null:s.text()}},Ta={user:null,req:null},Ns=class{constructor(t){this.nodes=t}static{a(this,"RedNodeTypesAdapter")}async listNodeSets(){if(typeof this.nodes.getNodeList!="function")return[];let t,n=this.nodes.getNodeList;try{t=await Promise.resolve(n(Ta))}catch{try{t=await Promise.resolve(n())}catch{return[]}}if(!Array.isArray(t))return[];let r=[];for(let o of t){if(!o||typeof o!="object"||Array.isArray(o))continue;let s=La(o);s&&r.push(s)}return r}async getNodeConfigHtml(t,n){if(typeof this.nodes.getNodeConfig!="function")return null;try{let r=await Promise.resolve(this.nodes.getNodeConfig({id:t,lang:n??"en-US",...Ta}));if(typeof r=="string"&&r.length>0)return r}catch{}try{let r=await Promise.resolve(this.nodes.getNodeConfig(t,n));return typeof r=="string"&&r.length>0?r:null}catch{return null}}};a(Na,"createNodeTypesAdapter");a(Ms,"flattenInstalledTypes");a(ap,"matchesQuery");a(po,"listNodeTypes");a(dp,"extractHelpScriptBody");a(Ra,"removeHtmlBlocks");a(lp,"stripHelpText");a(cp,"parseRegisterTypeFromHtml");a(Pa,"parseRegisterTypeAt");a(up,"splitHelpSections");a(lr,"extractBalancedObject");a(_a,"matchStringProp");a(Ca,"matchNumberProp");a(pp,"parseDefaultsBlock");a(fp,"unescapeJsStringEscape");a(hp,"normalizeDefaultValue");a(uo,"parseJsLiteral");a(Is,"measureJsLiteralLength");a(Fa,"extractBalancedArray");a(mp,"parseLooseValue");a(gp,"findSetForType");a(Ds,"findCaseInsensitiveType");a(jt,"getNodeTypeDetail");a(Oa,"assertKnownNodeTypes");a($a,"assertKnownNodeType");Fe=null;a(yp,"defaultCatalogueFetcher");a(wp,"loadCatalogue");a(fo,"searchPalette");a(bp,"paletteMatchScore")});function lt(e){if(e.length===0)return;let t=e[0];if(!t)return;let n=e.map(s=>{let i={...s};return delete i.statusCode,i});if(e.length>1)throw new k(`${e.length} validation errors`,"validation_failed",{statusCode:400,details:{errors:n,errorCount:e.length}});let r={errors:n};for(let[s,i]of Object.entries(t))s==="code"||s==="error"||s==="errors"||s==="statusCode"||i!==void 0&&(r[s]=i);let o=typeof t.statusCode=="number"?t.statusCode:400;throw new k(t.error,t.code,{statusCode:o,details:r})}function Hs(e,t="payload.flow"){let n=new Map;for(let o of e)for(let s of o){if(!s||typeof s!="object"||Array.isArray(s))continue;let i=s.id;typeof i=="string"&&i.length>0&&n.set(i,(n.get(i)??0)+1)}let r=[];for(let[o,s]of n)s>1&&r.push({code:"duplicate_node_id",error:`Node id '${o}' appears ${s} times in ${t}; node ids must be unique`,nodeId:o,count:s});return r}function vp(e){let t=typeof e.label=="string"&&e.label?` '${e.label}'`:"";return e.type==="tab"?`tab${t||` '${e.id}'`}`:e.type==="subflow"?`subflow${t||` '${e.id}'`}`:typeof e.z=="string"&&e.z?`${e.type} node on '${e.z}'`:`global ${e.type} config node`}function Da(e,t,n){let r=new Map;for(let i of n)!i||typeof i.id!="string"||i.id===t||i.z===t||r.set(i.id,i);let o=[],s=new Set;for(let i of[e.nodes,e.configs])if(Array.isArray(i))for(let d of i){let l=d&&typeof d=="object"?d.id:void 0;if(typeof l!="string"||!l||s.has(l))continue;if(s.add(l),l===t){o.push({code:"duplicate_node_id",error:`Node id '${l}' equals the tab id; node ids must be unique across all flows`,nodeId:l});continue}let c=r.get(l);c&&o.push({code:"duplicate_node_id",error:`Node id '${l}' is already used by ${vp(c)}; node ids must be unique across all flows`,nodeId:l})}return o}function Ma(e){let t=new Map;for(let n of[e.nodes,e.configs])if(Array.isArray(n))for(let r of n){if(!r||typeof r!="object"||Array.isArray(r))continue;let o=r;typeof o.id=="string"&&t.set(o.id,o)}return t}function Ap(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]);for(let r of n)if(!kp.has(r)&&JSON.stringify(e[r])!==JSON.stringify(t[r]))return!0;return!1}function xp(e,t){let n=Ma(t);if(!e)return new Set(n.keys());let r=Ma(e),o=new Set;for(let[s,i]of n){let d=r.get(s);(!d||Ap(d,i))&&o.add(s)}return o}function Ha(e,t){let n=[],r=e;typeof r.id!="string"||r.id.length===0?n.push({code:"missing_flow_id",error:"payload.flow.id is required and must match payload.flowId"}):r.id!==t&&n.push({code:"invalid_flow_id",error:`payload.flow.id ('${r.id}') must match payload.flowId ('${t}')`}),"nodes"in r?Array.isArray(r.nodes)||n.push({code:"invalid_nodes",error:"payload.flow.nodes must be an array"}):n.push({code:"invalid_nodes",error:"payload.flow.nodes must be an array (use [] to clear the tab intentionally)"}),"configs"in r&&r.configs!==void 0&&!Array.isArray(r.configs)&&n.push({code:"invalid_nodes",error:"payload.flow.configs must be an array"});let o=[];Array.isArray(r.nodes)&&o.push({key:"nodes",list:r.nodes}),Array.isArray(r.configs)&&o.push({key:"configs",list:r.configs}),n.push(...Hs(o.map(s=>s.list)));for(let{key:s,list:i}of o)for(let d=0;d<i.length;d+=1){let l=i[d];if(!l||typeof l!="object"||Array.isArray(l)){n.push({code:"invalid_node",error:`payload.flow.${s}[${d}] must be an object`});continue}let c=l;(typeof c.id!="string"||c.id.length===0)&&n.push({code:"missing_node_id",error:`payload.flow.${s}[${d}] needs a non-empty string id`});try{bt(c.type,"node.type")}catch(u){u instanceof k&&n.push({code:u.code,error:u.message,...typeof c.id=="string"?{nodeId:c.id}:{}})}"wires"in c&&kn(c.wires,n,typeof c.id=="string"?c.id:void 0)}return n}function kn(e,t,n){if(!Array.isArray(e))return t.push({code:"invalid_wires",error:"wires must be an array of output ports (each a list of node ids)",...n?{nodeId:n}:{}}),!1;for(let r of e){if(!Array.isArray(r))return t.push({code:"invalid_wires",error:"wires must be an array of output ports (each a list of node ids)",...n?{nodeId:n}:{}}),!1;for(let o of r)if(typeof o!="string"||o.length===0)return t.push({code:"invalid_wires",error:"wires targets must be non-empty node id strings",...n?{nodeId:n}:{}}),!1}return!0}async function Sp(e,t){if(typeof e.outputs=="number"&&Number.isFinite(e.outputs)&&e.outputs>=0)return Math.trunc(e.outputs);let n=typeof e.type=="string"?e.type:"";if(!(!t||!n))try{return(await jt(t,n,{includeHelp:!1})).outputs}catch{return}}function Tp(e,t,n){let r=typeof e.type=="string"?e.type:"node";if(!(n.length<=t))return t===0?{code:"invalid_wires",error:`${r} has 0 outputs; wires must be []`,nodeId:e.id,type:r}:{code:"invalid_wires",error:`${r} has ${t} outputs; wires has ${n.length} port arrays (max ${t})`,nodeId:e.id,type:r}}function Rp(e){return Array.isArray(e)&&e.some(t=>typeof t=="string"&&t.length>0)}function Pp(e,t){return t<=0?!1:!("wires"in e)||e.wires===void 0?!0:Array.isArray(e.wires)?!e.wires.some(Rp):!1}async function js(e,t,n={}){let r=[],o=[],s=new Set(e.map(d=>d.id)),i=n.softWarningNodeIds;for(let d of e){let l=await Sp(d,t),c=typeof d.type=="string"?d.type:"node",u=!i||i.has(d.id);if("wires"in d&&d.wires!==void 0){if(!kn(d.wires,r,d.id))continue;let p=d.wires;if(l!==void 0){let f=Tp(d,l,p);f?(f.cause="excess_ports",r.push(f)):u&&p.length<l&&o.push({code:"incomplete_wires",error:`${c} '${d.id}' declares ${l} outputs but wires has only ${p.length} port(s)`,nodeId:d.id,type:c})}for(let f of p)for(let m of f){let h=String(m);s.has(h)||r.push({code:"invalid_wires",error:`Wire target '${h}' on node '${d.id}' was not found in this flow (add the node in the same patch or use an existing id)`,nodeId:d.id,cause:"missing_target",targetId:h})}}u&&l!==void 0&&Pp(d,l)&&o.push({code:"unwired_node",error:`Node '${d.id}' (${c}) has ${l} output(s) but is not wired to anything yet`,nodeId:d.id,type:c})}return{errors:r,warnings:o}}function Us(e,t){let n=[];for(let r of e)r.z===void 0||r.z===""||typeof r.z=="string"&&r.z!==t&&n.push({code:"z_mismatch",error:`Node '${r.id}' has z='${r.z}' but flow id is '${t}'`,nodeId:r.id});return n}function _p(e,t){return e.filter(n=>n.type===t).map(n=>n.id)}async function Bs(e,t,n){let r=[],o=[...t];for(let s of e){let i;try{i=bt(s.type,"node.type")}catch{continue}let d;try{d=await jt(n,i,{includeHelp:!1})}catch{continue}let l=d.defaults;if(!l||typeof l!="object")continue;let c=new Set(d.requiredConfigNodeTypes??[]);for(let[u,p]of Object.entries(l)){if(!p||typeof p!="object"||Array.isArray(p))continue;let f=p.type;if(typeof f!="string"||!f)continue;let h=p.required===!0||c.has(f),g=s[u],y=_p(o,f);if(typeof g!="string"||g.length===0){h&&r.push({code:"missing_config_node",error:`Node '${s.id}' (${i}) has no ${f} on '${u}' yet \u2014 configure before deploy if needed`,nodeId:s.id,type:i,property:u,configType:f,candidates:y});continue}o.find(v=>v.id===g)?.type!==f&&r.push({code:"missing_config_node",error:`Node '${s.id}' property '${u}' does not resolve to a ${f} config yet`,nodeId:s.id,type:i,property:u,configType:f,candidates:y})}}return r}async function ja(e,t={}){let n=t.flowId??(typeof e.id=="string"?e.id:""),r=Array.isArray(e.nodes)?e.nodes:[],o=Array.isArray(e.configs)?e.configs:[],s=[],i=[];n&&s.push(...Us(r,n));let d=t.baseFlow?xp(t.baseFlow,e):void 0,l=await js(r,t.nodeTypes,{...d?{softWarningNodeIds:d}:{}});if(s.push(...l.errors),i.push(...l.warnings),t.nodeTypes){let c=await Bs(r,o,t.nodeTypes);d?i.push(...c.filter(u=>!u.nodeId||d.has(u.nodeId))):i.push(...c)}return{errors:s,warnings:i}}var kp,qs=q(()=>{"use strict";se();Ut();a(lt,"throwValidationIssues");a(Hs,"collectDuplicateNodeIdIssues");a(vp,"describeLiveNode");a(Da,"collectCrossFlowDuplicateIssues");kp=new Set(["x","y","w","h","g","id","z"]);a(Ma,"collectNodesMap");a(Ap,"nodeContentChanged");a(xp,"changedNodeIds");a(Ha,"collectFullFlowShapeIssues");a(kn,"assertValidWiresShape");a(Sp,"resolveNodeOutputCount");a(Tp,"wireCountIssue");a(Rp,"portHasTargets");a(Pp,"nodeIsUnwired");a(js,"collectWireValidation");a(Us,"collectZMismatchIssues");a(_p,"configCandidates");a(Bs,"collectConfigWarnings");a(ja,"validateFlowTab")});function vt(e,t="node"){return typeof e=="string"&&e.trim()?e.trim():t}function ho(e){let t=new Map;for(let n of e)n.type==="tab"&&typeof n.id=="string"&&typeof n.label=="string"&&n.label.trim()&&t.set(n.id,n.label.trim());return t}function Ba(e,t){e[Ua]=t}function Ip(e){if(!e)return;let t=e[Ua];return t instanceof Map?t:void 0}function Lp(e){return typeof e!="string"||!e.trim()?new Set:new Set(e.split(",").map(t=>t.trim()).filter(Boolean))}function Np(e){if(e===!0||e===1)return!0;if(typeof e=="string"){let t=e.trim().toLowerCase();return t==="1"||t==="true"||t==="yes"}return!1}function ge(e){let t=Lp(e.include);return Np(e.include_payload)&&(t.add("payload"),t.add("baseFlow")),t}function V(e,t){let n={req:e};t!==void 0&&(n.httpAdminRoot=t);let r=Ip(e);return r&&(n.liveFlowLabels=r),n}function An(e){return typeof e.name=="string"&&e.name.trim()?e.name.trim():typeof e.label=="string"&&e.label.trim()?e.label.trim():""}function cr(e){return`"${e.replace(/"/g,"'")}"`}function xn(e){let t=new Map;for(let n of e.nodes??[])t.set(n.id,n);for(let n of e.configs??[])t.set(n.id,n);return t}function Fp(e,t){let n=new Set(["x","y","w","h","g","id","z"]),r=new Set([...Object.keys(e),...Object.keys(t)]);for(let o of r)if(!n.has(o)&&JSON.stringify(e[o])!==JSON.stringify(t[o]))return!0;return!1}function Op(e,t){let n=An(e),r=An(t);if(!n||!r||n===r)return!1;let o=new Set(["x","y","w","h","g","id","z","name","label"]),s=new Set([...Object.keys(e),...Object.keys(t)]);for(let i of s)if(!o.has(i)&&JSON.stringify(e[i])!==JSON.stringify(t[i]))return!1;return!0}function zs(e,t){let n=[],r=[],o=[];for(let[s,i]of t)e.has(s)||n.push(i);for(let[s,i]of e)t.has(s)||r.push(i);for(let[s,i]of t){let d=e.get(s);d&&Fp(d,i)&&o.push({before:d,after:i})}return{added:n,removed:r,modified:o}}function mo(e){return e===1?"node":"nodes"}function $p(e){return e.length===1&&e[0]?`adds 1 ${vt(e[0].type)}`:`adds ${e.length} ${mo(e.length)}`}function Mp(e){return`deletes ${e} ${mo(e)}`}function Dp(e){return`edits ${e} ${mo(e)}`}function Hp(e){let t=[];e.removed>0&&t.push(Mp(e.removed));let n=e.added??[],r=e.addedCount??n.length;r>0&&t.push(n.length===r?$p(n):`adds ${r} ${mo(r)}`),(e.modified??0)>0&&t.push(Dp(e.modified??0));let o=t.length?t.join(", "):"replaces nodes",s=e.flowLabel?.trim();return s?`Replace ${s}: ${o}`:`Replace flow: ${o}`}function jp(e){let{added:t,removed:n,modified:r}=e;if(t.length+n.length+r.length!==1)return;if(t[0]){let u=t[0],p=vt(u.type),f=An(u);return f?`Add ${p} node ${cr(f)}`:`Add ${p} node`}if(n[0]){let u=n[0],p=vt(u.type),f=An(u);return f?`Remove ${p} node ${cr(f)}`:`Remove ${p} node`}let s=r[0];if(!s)return;let i=vt(s.after.type,vt(s.before.type)),d=An(s.before),l=An(s.after);if(Op(s.before,s.after)&&d&&l)return`Rename ${i} node ${cr(d)} \u2192 ${cr(l)}`;let c=l||d;return c?`Edit ${i} ${cr(c)}`:`Edit ${i}`}function Ws(e,t){return typeof e?.label=="string"&&e.label.trim()?e.label.trim():typeof t?.label=="string"&&t.label.trim()?t.label.trim():""}function go(e,t){if(typeof e.flowLabel=="string"&&e.flowLabel.trim())return e.flowLabel.trim();let n=Ws(e.payload?.flow,e.baseFlow);if(n)return n;let r=e.payload?.flowId;return r&&t?.has(r)?t.get(r)??"":""}function qa(e,t){let n=t.trim(),r=e.trim();if(!n||!r||r.includes(n))return r;let o=r.lastIndexOf(" in ");if(o>=0){let s=r.slice(o+4);if(s.length>0&&n.startsWith(s))return r}return`${r} in ${n}`}function Gs(e,t){let n=zs(xn(e??{}),xn(t)),r=n.added.length+n.removed.length+n.modified.length,o=Ws(t,e);if(r===0)return o?`Update ${o}`:"Update flow";if(n.removed.length>0)return Hp({...o?{flowLabel:o}:{},removed:n.removed.length,added:n.added,modified:n.modified.length});if(r>Ep)return o?`${r} changes in ${o}`:`${r} changes`;let s=jp(n)??(o?`Update ${o}`:"Update flow");return qa(s,o)}function Up(e,t){let n=(e??"").trim();return!!(!n||n.includes("[object Object]")||/^Update(\s|$)/i.test(n)||t>0&&!/^Replace\b/i.test(n))}function Ks(e,t){return`${e} \xB7 ${qp(t)}`}function Bp(e){let t=e>=0?"+":"-",n=Math.abs(e),r=String(Math.floor(n/60)).padStart(2,"0"),o=String(n%60).padStart(2,"0");return`${t}${r}:${o}`}function qp(e,t=new Date(e)){let n=Number.isNaN(t.getTime())?new Date(e):t;if(Number.isNaN(n.getTime()))return e.slice(0,16);let r=n.getDate(),s=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"][n.getMonth()]??"Jan",i=String(n.getHours()).padStart(2,"0"),d=String(n.getMinutes()).padStart(2,"0"),l=Bp(-n.getTimezoneOffset());return`${r} ${s} ${i}:${d} ${l}`}function ur(e){if(typeof e!="string")return;let t=e.replace(/\0/g,"").replace(/[\u0001-\u001F\u007F]/g," ").replace(/\s+/g," ").trim().slice(0,Cp).trim();return t.length>0?t:void 0}function zp(e,t){let n=Ae(t);return`${n==="/"?"/ai-access":`${n}/ai-access`}#proposal=${encodeURIComponent(e)}`}function Js(e,t={}){let n=go(e,t.liveFlowLabels),r=ur(e.title),o=Gs(e.baseFlow,n&&!Ws(e.payload.flow,e.baseFlow)?{...e.payload.flow,label:n}:e.payload.flow),s=zs(xn(e.baseFlow??{}),xn(e.payload.flow)),i=Up(r,s.removed.length)?o:r??o;return qa(i,n)}function Wp(e){return typeof e.storedTitle=="string"&&e.storedTitle?e.storedTitle:ur(e.title)}function ue(e,t={}){let n=Js(e,t),r=go(e,t.liveFlowLabels),o=Wp(e)??n,s=zp(e.id,t.httpAdminRoot),i=t.req?ar(t.req,s):s;return{...e,...e.conflictChoice?{conflictChoice:{nodeId:e.conflictChoice.nodeId,choice:e.conflictChoice.choice,...e.conflictChoice.chooser?{chooser:e.conflictChoice.chooser}:{}}}:{},title:n,storedTitle:o,flowLabel:r,displayName:Ks(n,e.createdAt),reviewUrl:i,reviewPath:s}}function Gp(e){let t=zs(xn(e.baseFlow??{}),xn(e.payload.flow)),n=[];return t.added.length>0&&n.push(t.added.length===1?"Adds 1 node":`Adds ${t.added.length} nodes`),t.removed.length>0&&n.push(t.removed.length===1?"Deletes 1 node":`Deletes ${t.removed.length} nodes`),t.modified.length>0&&n.push(t.modified.length===1?"Edits 1 node":`Edits ${t.modified.length} nodes`),n.length===0?"No node changes":n.map((r,o)=>o===0?r:r.charAt(0).toLowerCase()+r.slice(1)).join(", ")}function Kp(e){if(e.kind==="oauth")return{kind:"oauth",clientId:e.clientId,user:e.user};let t={kind:e.kind,linkId:e.linkId};return typeof e.label=="string"&&e.label.length>0&&(t.label=e.label),t}function ie(e,t={}){let n=ue(e,t),r=t.include??new Set,o={id:n.id,status:n.status,type:n.type,title:n.title,storedTitle:n.storedTitle,displayName:n.displayName,flowLabel:n.flowLabel,reviewUrl:n.reviewUrl,reviewPath:n.reviewPath,createdAt:n.createdAt,updatedAt:n.updatedAt,expectedRev:n.expectedRev,baseRev:n.expectedRev,createdBy:Kp(n.createdBy),changeSummary:Gp(e)};return r.has("payload")&&(o.payload=n.payload),r.has("baseFlow")&&n.baseFlow!==void 0&&(o.baseFlow=n.baseFlow),n.warnings&&n.warnings.length>0&&(o.warnings=n.warnings),typeof e.payload.flowId=="string"&&(o.flowId=e.payload.flowId),e.restoredSecrets&&e.restoredSecrets.length>0&&(o.restoredSecrets=e.restoredSecrets),n.deployPending&&(o.deployPending=n.deployPending),n.aiConflictOverrides&&(o.aiConflictOverrides=n.aiConflictOverrides),n.conflictChoice&&(o.conflictChoice=n.conflictChoice),n.rebase&&(o.rebase=n.rebase),n.rebase&&(n.status==="pending"||n.status==="conflict")&&(o.baseRevChanged=n.expectedRev!==n.rebase.rev),n.baseRevChanged!==void 0&&(o.baseRevChanged=n.baseRevChanged),n.decidedBy!==void 0&&(o.decidedBy=n.decidedBy),n.reason!==void 0&&(o.reason=n.reason),n.resultRev!==void 0&&(o.resultRev=n.resultRev),o}function za(e,t={}){return e.map(n=>ie(n,t))}var Cp,Ep,Ua,Oe=q(()=>{"use strict";ve();ze();Cp=80,Ep=1;a(vt,"nodeTypeText");Ua=Symbol.for("ai-access.liveFlowLabels");a(ho,"liveFlowLabelsFromFlows");a(Ba,"setRequestFlowLabels");a(Ip,"getRequestFlowLabels");a(Lp,"parseIncludeQuery");a(Np,"isTruthyFlag");a(ge,"resolveIncludeFromQuery");a(V,"presentOptionsFor");a(An,"nodeLabel");a(cr,"quoteName");a(xn,"collectNodes");a(Fp,"nodeContentChanged");a(Op,"isRenameOnly");a(zs,"classifyFlowChanges");a(mo,"pluralNodes");a($p,"describeAddedClause");a(Mp,"describeRemovedClause");a(Dp,"describeModifiedClause");a(Hp,"buildReplaceFlowTitle");a(jp,"describeOneChange");a(Ws,"flowLabelHint");a(go,"resolveFlowLabel");a(qa,"ensureTitleIncludesFlowName");a(Gs,"autoGenerateProposalTitle");a(Up,"shouldRefreshDisplayTitle");a(Ks,"buildProposalDisplayName");a(Bp,"formatUtcOffset");a(qp,"formatProposalSubmittedAt");a(ur,"sanitizeProposalTitle");a(zp,"buildProposalReviewPath");a(Js,"resolveProposalTitle");a(Wp,"storedTitleOf");a(ue,"presentProposal");a(Gp,"buildChangeSummary");a(Kp,"presentCreatedByForApi");a(ie,"presentProposalForApi");a(za,"presentProposalsForApi")});function gr(e){let t=e.join("\0"),n=yo.get(t);if(n)return n;let r=[...new Set(e.map(d=>zr(d).join("")).filter(Boolean))],o=new Map,s={patterns:[...e],matches(d){if(typeof d!="string"||d.length===0||r.length===0)return!1;let l=o.get(d);if(l!==void 0)return l;let c=i(d);return o.size>4096&&o.clear(),o.set(d,c),c}};function i(d){{let l=zr(d),c=l[l.length-1];if(!c||Jp.has(c))return!1;for(let u of r){let p=`${u}S`;for(let f=0;f<l.length;f++){let m="";for(let g=f;g<l.length;g++){if(m+=l[g],m===u||m===p)return!0;if(m.length>p.length)break}let h=l[f]??"";if(u.length>=6&&(h.endsWith(u)||h.endsWith(p)))return!0}}return!1}}return a(i,"matchUncached"),yo.size>32&&yo.clear(),yo.set(t,s),s}function kt(){return aa()?.getEffective()?.secretNamePatterns??Ot}function fr(e){let t=e.trim();return!(!t||t===J||Vp.has(t.toLowerCase())||/^\$\{[A-Za-z0-9_]+\}$/.test(t))}function Xp(e){let t=Ka.get(e);return t||(t=Zp(e),Ka.set(e,t)),t}function Zp(e){return[{hints:["Buffer.from","btoa"],re:/\b(?:Buffer\.from|btoa)\([ \t]{0,8}(?<q>["'`])[^"'`:\s]{1,256}:(?<s>[^"'`\s]{1,1024})\k<q>/dg,accept:a(t=>!t.includes("${"),"accept")},{hints:["PRIVATE KEY-----"],re:/-----BEGIN ((?:[A-Z0-9]{1,16} ){0,4})PRIVATE KEY-----(?<s>[^-]{1,16384})-----END \1PRIVATE KEY-----/dg},{hints:["AKIA","ASIA","ABIA","ACCA"],re:/(?<![A-Za-z0-9])(?<s>(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16})(?![A-Za-z0-9])/dg},{hints:["ghp_","gho_","ghu_","ghs_","ghr_"],re:/(?<![A-Za-z0-9_])(?<s>gh[pousr]_[A-Za-z0-9]{36,255})(?![A-Za-z0-9_])/dg},{hints:["github_pat_"],re:/(?<![A-Za-z0-9_])(?<s>github_pat_[A-Za-z0-9_]{22,255})(?![A-Za-z0-9_])/dg},{hints:["xox"],re:/(?<![A-Za-z0-9_-])(?<s>xox[abposr]-[A-Za-z0-9-]{10,512})/dg},{hints:["hooks.slack.com"],re:/hooks\.slack\.com\/services\/(?<s>[A-Za-z0-9_/]{16,512})/dg},{hints:["sk-"],re:/(?<![A-Za-z0-9_-])(?<s>sk-[A-Za-z0-9_-]{20,512})/dg},{hints:["k_live_","k_test_"],re:/(?<![A-Za-z0-9_])(?<s>[sr]k_(?:live|test)_[A-Za-z0-9]{16,512})/dg},{hints:["AIza"],re:/(?<![A-Za-z0-9_-])(?<s>AIza[0-9A-Za-z_-]{35})(?![A-Za-z0-9_-])/dg},{hints:["eyJ"],re:/(?<![A-Za-z0-9_-])(?<s>eyJ[A-Za-z0-9_-]{5,8192}\.[A-Za-z0-9_-]{5,8192}\.[A-Za-z0-9_-]{5,8192})/dg},{hints:["earer"],re:/\b[Bb]earer[ \t]{1,8}(?<s>(?=[A-Za-z0-9\-._~+/]{0,4096}[0-9\-._~+/])[A-Za-z0-9\-._~+/]{16,4096}={0,3})/dg},{hints:["asic"],re:/\b[Bb]asic[ \t]{1,8}(?<s>(?=[A-Za-z0-9+/]{0,4096}[0-9+/])[A-Za-z0-9+/]{8,4096}={0,2})(?![A-Za-z0-9+/])/dg},{hints:["@"],re:/\b[A-Za-z][A-Za-z0-9+.-]{1,20}:\/\/[^\s:@/'"`<>]{1,256}:(?<s>[^\s@/'"`<>]{1,1024})@/dg,accept:a(t=>!/^(?:\$\{|\{\{|<|%)/.test(t)&&t!==J,"accept")},{hints:["?","&"],re:/[?&](?<k>[A-Za-z_][\w.-]{0,63})=(?<s>[^&#\s"'`<>]{1,2048})/dg,accept:a((t,n)=>e.matches(n.groups?.k??"")&&fr(rf(t)),"accept")},{hints:[":","="],re:/(?<![\w$-])(?<q>["'`]?)(?<k>[A-Za-z_$][\w$-]{0,63})\k<q>\]?[ \t]{0,8}(?::|=(?![=>]))[ \t]{0,8}(?<vq>["'`])(?<s>(?:\\.|(?!\k<vq>)[^\\\r\n]){1,1024})\k<vq>/dg,accept:a((t,n)=>{let r=n.groups?.k??"";return!e.matches(r)||n.groups?.vq==="`"&&t.includes("${")||t.length<3||/\s/.test(t)?!1:fr(t)},"accept")}]}function ef(e){let t=new Map;for(let r of e)t.set(r,(t.get(r)??0)+1);let n=0;for(let r of t.values()){let o=r/e.length;n-=o*Math.log2(o)}return n}function tf(e){return/[^A-Za-z]/.test(e)||e.length>=16?!0:[/[a-z]/,/[A-Z]/,/[0-9]/,/[^A-Za-z0-9]/].filter(n=>n.test(e)).length>=3||ef(e)>=3}function nf(e){let t=e.patterns.filter(r=>Qp.includes(r.toUpperCase())),n=gr(t);return{hints:[":","="],re:/(?<![\w$.-])(?<k>[A-Za-z_][\w-]{0,63})[ \t]{0,8}(?::(?!\/\/)|=)[ \t]{0,8}(?<s>[^\s;,'"`&<>(){}[\]]{3,1024})/dg,accept:a((r,o)=>e.matches(o.groups?.k??"")&&fr(r)&&!r.startsWith("$")&&!r.startsWith("{{")&&(n.matches(o.groups?.k??"")||tf(r))&&!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/.test(r)&&o.input.charAt(o.indices?.groups?.s?.[1]??0)!=="(","accept")}}function rf(e){try{return decodeURIComponent(e)}catch{return e}}function of(e,t,n){let r=e.indexOf("=",t);if(r<=t||r>=n||r-t>48)return;let o=e.slice(t,r),s=Math.max(o.lastIndexOf('"'),o.lastIndexOf("'"),o.lastIndexOf("`")),i=o.slice(s+1).trim();if(!/^[A-Za-z][\w -]{0,40}$/.test(i))return;let d=r+1;for(;d<n&&(e[d]===" "||e[d]===" ");)d++;let l=d;for(;l<n&&!"\"'`\r".includes(e[l]??"");)l++;for(;l>d&&(e[l-1]===" "||e[l-1]===" ");)l--;return{k:i,vs:d,ve:l}}function sf(e,t){if(!e.includes(";")||!e.includes("="))return[];let n=[],r=0;for(;r<=e.length;){let o=e.indexOf(`
7
+ `,r);o<0&&(o=e.length);let s=[],i=r,d=!1;for(;i<o;){let l=e.indexOf(";",i);l<0||l>o?l=o:d=!0;let c=of(e,i,l);c&&s.push(c),i=l+1}if(d&&s.length>=2)for(let l of s)l.ve<=l.vs||!t.matches(l.k)||fr(e.slice(l.vs,l.ve))&&n.push({start:l.vs,end:l.ve});r=o+1}return n}function af(e,t,n={}){if(e.length===0)return{text:e,secrets:[],spans:[]};let r=[],o=Xp(t);if(!n.code){let u=Ja.get(t);u||(u=nf(t),Ja.set(t,u)),o=[...o,u]}r.push(...sf(e,t));for(let u of o){if(u.hints&&!u.hints.some(f=>e.includes(f)))continue;u.re.lastIndex=0;let p;for(;(p=u.re.exec(e))!==null;){let f=p.indices?.groups?.s,m=p.groups?.s;f&&m!==void 0&&m.length>0&&(!u.accept||u.accept(m,p))&&r.push({start:f[0],end:f[1]}),p[0].length===0&&u.re.lastIndex++}}if(r.length===0)return{text:e,secrets:[],spans:[]};r.sort((u,p)=>u.start-p.start||p.end-u.end);let s=[];for(let u of r){let p=s[s.length-1];p&&u.start<p.end?p.end=Math.max(p.end,u.end):s.push({...u})}let i="",d=0,l=[],c=[];for(let u of s){let p=e.slice(u.start,u.end);p!==J&&(i+=e.slice(d,u.start)+J,l.push(p),c.push(u),d=u.end)}return i+=e.slice(d),{text:i,secrets:l,spans:c}}function Zs(e,t){let n=[],r=0,o=a(s=>{let i=ko(s);for(let d=0;d<i;d++)n.push({kind:"literal",value:J})},"literals");for(let s of t)o(e.slice(r,s.start)),n.push({kind:"secret",value:e.slice(s.start,s.end)}),r=s.end;return o(e.slice(r)),n}function ee(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function df(e){return ee(e)&&typeof e.id=="string"&&typeof e.type=="string"}function Qs(e,t){let n=new Ys(t,e.id),r=e[pr];if(Array.isArray(r))for(let s of r)typeof s=="string"&&n.paths.push(s);let o={};for(let[s,i]of Object.entries(e))s!==pr&&(Xa.has(s)?o[s]=i:ed.has(s)&&Array.isArray(i)?o[s]=i.map(d=>ei(d,t)):s==="env"&&Array.isArray(i)?o[s]=n.env(i,"env"):o[s]=n.value(i,s,s,e));return n.paths.length>0&&(o[pr]=n.paths),o}function ei(e,t){return Array.isArray(e)?e.map(n=>ei(n,t)):df(e)?Qs(e,t):e}function ct(e,t=kt()){let n={matcher:gr(t),count:0};return{value:ei(e,n),count:n.count}}function ti(e,t=kt()){return e.map(n=>{if(n.nodeName===void 0)return{...n};let r=ct({id:"conflict",type:"node",name:n.nodeName},t);return{...n,nodeName:r.value.name}})}function Ye(e,t=kt()){let n={...e,payload:{...e.payload,flow:ct(e.payload.flow,t).value}};return e.conflictChoice&&(n.conflictChoice={nodeId:e.conflictChoice.nodeId,choice:e.conflictChoice.choice,...e.conflictChoice.chooser?{chooser:e.conflictChoice.chooser}:{}}),e.baseFlow!==void 0&&(n.baseFlow=ct(e.baseFlow,t).value),e.rebase&&(n.rebase={...e.rebase,conflicts:ti(e.rebase.conflicts,t)}),n}function At(e,t=kt){if(td(e))return e;let n=t===kt,r=n?Va.get(e):void 0;if(r)return r;let o={kind:e.kind,[ni]:e,async getFlows(){let s=await e.getFlows();return{rev:s.rev,flows:ct(s.flows,t()).value}},async getFlow(s){let i=await e.getFlow(s);return{rev:i.rev,flow:ct(i.flow,t()).value}},updateFlow:a(s=>e.updateFlow(s),"updateFlow")};return n&&Va.set(e,o),o}function td(e){return ni in e}function $e(e){return td(e)?e[ni]:e}function Xe(e,t){return e.accessLink||e.auth?At(t):t}function ko(e){let t=0,n=e.indexOf(J);for(;n!==-1;)t++,n=e.indexOf(J,n+J.length);return t}function hr(e){let t=e.split(J),n=[];for(let r=1;r<t.length;r++){let o=t[r-1]??"",s=t[r]??"",i=o.lastIndexOf(`
8
8
  `),d=s.indexOf(`
9
- `);n.push({before:i>=0?o.slice(i+1):o,after:d>=0?s.slice(0,d):s})}return n}function go(e){return JSON.stringify([e.before,e.after])}function ho(e){let t=new Map;return ur(e.text).forEach((n,r)=>{let o=go(n),s=t.get(o)??[];s.push(r),t.set(o,s)}),t}function Qa(e,t,n,r){if(e===t)return{text:n,restored:r.filter(f=>f.kind==="secret").length};let o=ur(e),s=ur(t);if(o.length>s.length)return{error:`contains ${o.length} [REDACTED] placeholders but the original had ${s.length}`};let i=ho({text:t,occurrences:r}),d=new Set([...i].filter(([,f])=>new Set(f.map(h=>r[h]?.kind)).size>1).map(([f])=>f)),l=new Map,c=e.split(J),u=c[0]??"",p=0;for(let f=0;f<o.length;f++){let h=o[f];if(!h)break;let m=go(h),g=i.get(m)??[];if(d.has(m))return{error:"ambiguous literal and secret [REDACTED] occurrences"};let y=l.get(m)??0,A=g[y]??-1;if(A<0)return{error:"a [REDACTED] placeholder is not on an unchanged original line"};l.set(m,y+1);let x=r[A];x?.kind==="secret"&&p++,u+=(x?.value??J)+(c[f+1]??"")}return{text:u,restored:p}}function vt(e){if(Z(e))for(let t of sf){let n=e[t];if(typeof n=="string"&&n.length>0)return`${t}=${n}`}}function fr(e,t){let n=a(s=>[s,...s.nodes??[],...s.configs??[]],"entities"),r=new Map(n(e).map(s=>[s.id,s])),o=[];for(let s of n(t)){let i=r.get(s.id);if(JSON.stringify(i?.env)===JSON.stringify(s.env))continue;let d=a(l=>{let c=new Set;return Array.isArray(l)?l.some(u=>!Z(u)||typeof u.name!="string"||!u.name?!1:c.has(u.name)?!0:(c.add(u.name),!1)):!1},"hasDuplicates");!d(i?.env)&&!d(s.env)||o.push({code:"duplicate_env_keys",...s.id?{nodeId:s.id}:{},property:"env",error:"Duplicate env names make pairing by name ambiguous when entries are reordered. Review the env values before approval."})}return o}function Qs(e,t,n,r=[]){let o=a(c=>{let u=new Map;return yo(c,{matcher:kn(dt()),count:0,fields:u}),u},"fieldsFor"),s=o(e),i=o(t),d=o(n),l=a((c,u,p,f,h=f,m=f)=>{if(typeof u=="string"){let g=s.get(`${e.id}\0${f}`);if(!g?.occurrences.some(E=>E.kind==="secret"))return u;let y=i.get(`${t.id}\0${h}`),A=d.get(`${n.id}\0${m}`),x=ur(g.text),$=ho(g),L=y?ho(y):new Map,D=A?ho(A):new Map,H=y?.occurrences.map(E=>E.value)??[],T=y?ur(y.text):[],k=y?.occurrences.some((E,v)=>{let S=T[v];return E.kind==="secret"&&S&&!$.has(go(S))}),_=!1;return g.occurrences.forEach((E,v)=>{if(E.kind!=="secret")return;let S=x[v],N=S?go(S):"",w=$.get(N)??[],F=L.get(N)??[],I=D.get(N)??[];if(w.length>1&&F.length>0&&F.length<w.length&&JSON.stringify(w.map(Bn=>g.occurrences[Bn]?.value))!==JSON.stringify(I.map(Bn=>A?.occurrences[Bn]?.value)))throw new pr;let C=a((Bn,Jc)=>{let Gi=Bn.get(N)??[],[fs]=Gi;return Gi.length===1&&fs!==void 0&&Jc?.occurrences[fs]?.kind==="secret"?fs:void 0},"matches"),ee=C($,g),ot=C(L,y),Un=C(D,A);if(!L.has(N)&&!k)return;if(ee===void 0||ot===void 0||Un===void 0){_=!0;return}let Wi=A?.occurrences[Un];y?.occurrences[ot]?.value===E.value&&Wi&&(H[ot]=Wi.value)}),_&&r.push({code:"secret_may_revert",nodeId:e.id,property:h,error:"Secret spans could not be aligned reliably. The proposed value was kept and may revert an editor secret change. Review this field before approval."}),!y||_?u:y.text.split(J).reduce((E,v,S)=>E+(S?H[S-1]??J:"")+v,"")}if(Array.isArray(u)){let g=Array.isArray(c)?c:[],y=Array.isArray(p)?p:[],A=new Set,x=new Set;return u.map(($,L)=>{let D=vt($),H=a((_,E)=>{let v=D===void 0?L:_.findIndex((S,N)=>!E.has(N)&&vt(S)===D);return E.add(v),v},"match"),T=H(g,A),k=H(y,x);return l(g[T],$,y[k],`${f}[${T}]`,`${h}[${L}]`,`${m}[${k}]`)})}if(Z(u)){let g=Z(c)?c:{},y=Z(p)?p:{},A=a((x,$)=>x?`${x}.${$}`:$,"path");return Object.fromEntries(Object.entries(u).map(([x,$])=>[x,l(g[x],$,y[x],A(f,x),A(h,x),A(m,x))]).filter(([,x])=>x!==void 0))}return u},"walk");return l(e,t,n,"")}function ed(e,t){let n=Vs(t,[]);return"error"in Qa(e,t,t,n)}function mo(e,t,n,r,o,s){if(typeof e=="string"){if(!e.includes(J))return e;let i=s.fields.get(`${r}\0${o}`);if(typeof t=="string"&&typeof n=="string"&&(i||n.includes(J))){let d=Qa(e,t,n,i?.occurrences??Vs(n,[]));return"error"in d?(s.unresolved.push({nodeId:r,property:o,reason:d.error}),e):(d.restored>0&&(s.restored+=d.restored,s.restoredFields.push({nodeId:r,property:o})),d.text)}return s.unresolved.push({nodeId:r,property:o,reason:"no redacted secret exists at this node id and field"}),e}if(Array.isArray(e)){let i=Array.isArray(t)?t:[],d=Array.isArray(n)?n:[],l=new Set;return e.map(c=>{let u=Z(c)&&c.redacted===!0?Js(c,"redacted"):c,p=vt(u),f=-1;if(p!==void 0){let h=d.map((m,g)=>vt(m)===p&&!l.has(g)?g:-1).filter(m=>m>=0);h.length>0&&(f=h[0]??-1)}return f>=0?(l.add(f),mo(u,i[f],d[f],r,`${o}[${f}]`,s)):mo(u,void 0,void 0,r,`${o}[?]`,s)})}if(Z(e)){let i=Z(t)?t:{},d=Z(n)?n:{},l={};for(let[c,u]of Object.entries(e))l[c]=mo(u,i[c],d[c],r,o?`${o}.${c}`:c,s);return l}return e}function Js(e,t){let n={...e};return delete n[t],n}function af(e){let t=Js(e,dr);return Array.isArray(t.env)&&(t.env=t.env.map(n=>!Z(n)||n.redacted!==!0?n:Js(n,"redacted"))),t}function td(e,t,n,r){let o=typeof e.id=="string"?e.id:"",s=o?t.get(o):void 0,i={},d=s?yo({...s,id:o},r):void 0;for(let[l,c]of Object.entries(e))if(l!==dr){if(Ya.has(l)&&Array.isArray(c)){i[l]=c.map(u=>Z(u)?td(u,t,n,r):u);continue}if(Ka.has(l)){i[l]=c;continue}i[l]=mo(c,d?.[l],s?.[l],o,l,n)}return af(i)}function nd(e,t,n=dt()){let r=new Map;for(let l of t)Z(l)&&typeof l.id=="string"&&r.set(l.id,l);let o=new Map,s={matcher:kn(n),count:0,fields:o},i={fields:o,restored:0,restoredFields:[],unresolved:[]};return{flow:td(e,r,i,s),restored:i.restored,restoredFields:i.restoredFields,unresolved:i.unresolved}}function uf(e){return typeof e.type=="string"&&lf.has(e.type)?!0:cf.some(t=>typeof e[t]=="string"&&e[t]!=="")}function Ga(e){let t=new Set,n=a((r,o)=>{if(!(o>12)){if(typeof r=="string"){if(r.length>Va)return;let s=/(?:\b(?:env|global|flow)\.get|\$env)\([ \t]{0,8}["'`]([^"'`\r\n]{1,128})["'`]|\$\{([A-Za-z_][\w.-]{0,127})\}/g,i;for(;(i=s.exec(r))!==null;)t.add(i[1]??i[2]??"");return}if(Array.isArray(r)){for(let s of r)n(s,o+1);return}if(Z(r))for(let[s,i]of Object.entries(r))[`${s}Type`,s==="v"?"vt":"",s==="to"?"tot":"",s==="value"?"type":"",s==="valueValue"?"valueType":"",s==="keyValue"?"keyType":""].some(c=>c&&typeof r[c]=="string"&&pf.has(r[c]))&&typeof i=="string"&&t.add(i),n(i,o+1)}},"walk");return n(e,0),t.delete(""),t}function lr(e,t){return JSON.stringify(e)===JSON.stringify(t)}function rd(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]);for(let r of n)if(!(ff.has(r)||r==="nodes"||r==="configs")&&!lr(e[r],t[r]))return!0;return!1}function hf(e,t,n){let r=new Set([n]),o=typeof e.g=="string"?e.g:void 0;for(let s=0;o&&s<32&&!r.has(o);s++){r.add(o);let i=t.get(o);o=i&&typeof i.g=="string"?i.g:void 0}for(let s of Object.values(e))typeof s=="string"&&s!==e.id&&t.get(s)?.z===void 0&&t.has(s)&&r.add(s);for(let[s,i]of t)i.type==="global-config"&&r.add(s);return[...r]}function mf(e,t){return e.filter(n=>n.nodeId===t).map(n=>n.property).join(", ")}function gf(e,t,n,r){let o=[],s=df.filter(d=>!lr(n[d],r[d]));s.length>0&&o.push({code:"secret_destination_changed",error:`SECURITY: node ${e} holds a secret (${t}) restored from the live flow, and this proposal changes where it sends data (${s.join(", ")}). Verify the new destination before approving.`,nodeId:e,property:s.join(",")});let i=[...Ja].filter(d=>!lr(n[d],r[d]));return i.length>0&&o.push({code:"secret_node_code_changed",error:`Node ${e} holds a secret (${t}) and its code changed (${i.join(", ")}). Check the new code does not send the secret anywhere unexpected.`,nodeId:e,property:i.join(",")}),lr(n.wires,r.wires)||o.push({code:"secret_wires_changed",error:`SECURITY: node ${e} holds a secret (${t}) and its output wires changed. Check where its messages (which may carry the secret) now go.`,nodeId:e,property:"wires"}),o}function yf(e,t,n,r,o,s){let i=[];for(let d of hf(t,n,o)){if(d===e||s.has(d))continue;let l=n.get(d),c=d===o,u=r.get(d)??(l&&!c&&l.z===void 0?l:void 0);if(!(c?!lr(l?.env,u?.env):l!==void 0&&u!==void 0&&rd(l,u)))continue;s.add(d);let f=c?"tab":typeof l?.type=="string"?l.type:"node";i.push({code:"secret_scope_changed",error:`SECURITY: ${f} ${d} scopes or configures secret-holding node ${e} (env vars like \${HOST} or a config node) and it changed. Check URLs/hosts it resolves to.`,nodeId:d,property:c?"env":"config"})}return i}function wf(e,t,n,r,o){let s=[],i=[];for(let[d,l]of n){if(d===r)continue;let c=t.get(d);if(c&&!rd(c,l))continue;e.includes(d)||i.push(d);let u=typeof l.type=="string"?l.type:"node";uf(l)&&!e.includes(d)&&s.push({code:"outbound_node_changed",error:`SECURITY: this proposal ${c?"changes":"adds"} outbound-capable node ${d} (${u}). Check where it sends data before approving.`,nodeId:d,property:"type"});let p=c?Ga(c):new Set,f=[...Ga(l)].filter(h=>!p.has(h)&&o.matches(h));f.length>0&&s.push({code:"secret_reference_added",error:`SECURITY: node ${d} (${u}) now reads secret-named value(s) ${f.slice(0,5).join(", ")} (env / global / flow). The AI cannot see these values but the flow will use them at runtime; check they are not sent anywhere unexpected.`,nodeId:d,property:f.slice(0,5).join(",")})}if(e.length===0)return s;for(let[d,l]of t)l.z===r&&!n.has(d)&&i.push(d);if(i.length>0){let d=i.slice(0,10).join(", ")+(i.length>10?", \u2026":"");s.push({code:"secret_tab_changed",error:`This proposal restores secrets in this tab and also changes ${i.length} other node(s) (${d}). Review every change: any of them could route secret data elsewhere.`,nodeId:r,property:"nodes"})}return s}function bf(e,t){let n=new Map;n.set(t,e);for(let r of["nodes","configs"]){let o=e[r];if(Array.isArray(o))for(let s of o)Z(s)&&typeof s.id=="string"&&n.set(s.id,s)}return n}function od(e,t,n,r=dt()){let o=[...new Set(e.map(u=>u.nodeId))],s=String(t.id),i=new Map;for(let u of n)Z(u)&&typeof u.id=="string"&&i.set(u.id,u);let d=bf(t,s),l=[],c=new Set;for(let u of o){let p=i.get(u),f=d.get(u);!p||!f||(l.push(...gf(u,mf(e,u),p,f)),l.push(...yf(u,p,i,d,s,c)))}return l.push(...wf(o,i,d,s,kn(r))),l}var dr,Wp,Gp,Ws,Ka,Kp,Ja,Va,Ya,Ua,Ba,fo,qa,Yp,za,Gs,Zs,Wa,sf,pr,df,lf,cf,pf,ff,Pe=q(()=>{"use strict";un();ke();un();dr="_aiRedactions",Wp=new Set(["TYPE","TYPES","MODE","METHOD","FIELD","FIELDS","PROP","PROPERTY","FORMAT","TIMEOUT","EXPIRY","EXPIRES","TTL","LENGTH","LEN","SIZE","COUNT","ENABLED","DISABLED","ENABLE","USE","URL","URI","ENDPOINT","HOST","PORT","PATH","HEADER","NAME","LABEL","FILE"]),Gp=new Set(["basic","digest","bearer","none","true","false","null","undefined","oauth","oauth2","jwt","apikey","api_key","token","password","header","query","body","cookie","msg","flow","global","env","str","num","json","bool","other"]),Ws=new Set(["env","msg","flow","global","jsonata","bool","date","node","cred"]),Ka=new Set(["id","type","z","g","x","y","w","h","wires","inputs","outputs","links","scope","icon","color","style","d","l","in","out","rev"]),Kp=new Set(["name","label","info"]),Ja=new Set(["func","initialize","finalize"]),Va=256*1024,Ya=new Set(["nodes","configs","subflows"]),Ua=["name","p","keyValue","key","property","k"],Ba={value:"type",v:"vt",to:"tot",valueValue:"valueType"},fo=new Map;a(kn,"createSecretNameMatcher");a(dt,"currentSecretNamePatterns");a(cr,"isSecretLikeValue");qa=new WeakMap;a(Jp,"contentRules");a(Vp,"buildContentRules");Yp=["PASSWORD","PASSWD","PASSPHRASE","SECRET","CLIENT_SECRET","TOKEN","API_KEY","APIKEY","PRIVATE","CREDENTIAL","PWD","PW"];a(Xp,"shannonEntropy");a(Zp,"looksRandom");a(Qp,"unquotedRule");a(ef,"decodeURIComponentSafe");a(tf,"parseConnSegment");a(nf,"connectionStringSpans");za=new WeakMap;a(rf,"scanTextForSecrets");a(Vs,"classifyPlaceholders");a(Z,"isPlainObject");a(of,"isFlowEntity");Gs=class{constructor(t,n){this.ctx=t;this.entityId=n}static{a(this,"EntityRedactor")}paths=[];record(t,n){this.paths.includes(t)||this.paths.push(t),this.ctx.count+=n.length||1,this.ctx.secrets?.set(`${this.entityId}\0${t}`,n)}string(t,n,r){if(r&&cr(t)||t.length>Va)return this.record(n,[t]),this.ctx.fields?.set(`${this.entityId}\0${n}`,{text:J,occurrences:[{kind:"secret",value:t}]}),J;let o=n.split(/[.[]/,1)[0]??"",s=rf(t,this.ctx.matcher,{code:Ja.has(o)});return this.ctx.fields&&s.text.includes(J)&&this.ctx.fields.set(`${this.entityId}\0${n}`,{text:s.text,occurrences:Vs(t,s.spans)}),s.secrets.length>0?(this.record(n,s.secrets),s.text):t}env(t,n){return t.map((r,o)=>{if(!Z(r))return r;let s=typeof r.type=="string"?r.type:"";if(s==="cred"||typeof r.value!="string"||Ws.has(s))return r;let i=typeof r.name=="string"?r.name:"",d=r.value,l=this.string(d,`${n}[${o}].value`,this.ctx.matcher.matches(i));return l===d?r:{...r,value:l,redacted:!0}})}value(t,n,r,o){if(typeof t=="string"){if(Kp.has(r))return this.string(t,n,!1);let s=o?.[`${r}Type`],i=typeof s=="string"&&Ws.has(s);return this.string(t,n,!i&&this.ctx.matcher.matches(r))}return Array.isArray(t)?r==="env"?this.env(t,n):t.map((s,i)=>this.value(s,`${n}[${i}]`,"",void 0)):Z(t)?this.object(t,n):t}object(t,n){let r=!1;for(let s of Ua){let i=t[s];if(typeof i=="string"&&this.ctx.matcher.matches(i)){r=!0;break}}let o={};for(let[s,i]of Object.entries(t)){let d=n?`${n}.${s}`:s;if(r&&s in Ba&&typeof i=="string"){let l=Ba[s],c=l?t[l]:void 0,u=typeof c=="string"&&Ws.has(c);o[s]=this.string(i,d,!u);continue}if(Ua.includes(s)&&typeof i=="string"){o[s]=this.string(i,d,!1);continue}o[s]=this.value(i,d,s,t)}return o}};a(yo,"redactEntity");a(Ys,"redactValueInner");a(bt,"redactFlowData");a(Ks,"normalizeSecretArrays");a(Xa,"secretValuesChanged");a(Xs,"redactConflictLabels");a(lt,"redactProposalForAi");Zs=Symbol.for("ai-access.rawFlowAdapter"),Wa=new WeakMap;a(kt,"aiRedactingAdapter");a(Za,"isRedactingAdapter");a(Oe,"rawFlowAdapter");a(Xe,"adapterForRequest");a(wo,"countRedactionPlaceholders");a(ur,"placeholderContexts");a(go,"contextKey");a(ho,"indexOccurrences");a(Qa,"restoreString");sf=["name","keyValue","p","property","key","k"];a(vt,"stableElementKey");a(fr,"duplicateEnvWarnings");pr=class extends Error{static{a(this,"SecretAlignmentConflictError")}constructor(){super("A duplicate secret line was removed while its live values changed. Resolve this field against the live flow."),this.name="SecretAlignmentConflictError"}};a(Qs,"preserveLiveSecrets");a(ed,"hasNewLiteralPlaceholder");a(mo,"restoreWalk");a(Js,"withoutKey");a(af,"stripMarkers");a(td,"restoreEntity");a(nd,"restoreRedactedPlaceholders");df=["type","url","host","hostname","server","broker","port","path","method","headers","endpoint","uri","protocol","proxy","tls","to","ret"],lf=new Set(["http request","http response","link out","link call","mqtt out","mqtt-broker","tcp out","tcp request","udp out","e-mail","websocket out","websocket-client","websocket-listener","function","exec","file"]),cf=["url","host","hostname","server","broker","endpoint","uri"];a(uf,"isOutboundNode");pf=new Set(["env","global","flow"]);a(Ga,"referencedNames");ff=new Set(["x","y","w","h","l"]);a(lr,"sameJson");a(rd,"nodeChanged");a(hf,"scopeIds");a(mf,"restoredFieldList");a(gf,"secretNodeWarnings");a(yf,"scopeWarnings");a(wf,"tabWarnings");a(bf,"proposedNodeMap");a(od,"secretChangeWarnings")});function mr(e){if(e===null||typeof e!="object")return JSON.stringify(e)??"undefined";if(Array.isArray(e))return`[${e.map(mr).join(",")}]`;let t=e;return`{${Object.keys(t).filter(r=>t[r]!==void 0).sort().map(r=>`${JSON.stringify(r)}:${mr(t[r])}`).join(",")}}`}function Af(e){return(0,ad.createHash)("sha256").update(mr(e)).digest("hex").slice(0,16)}function ye(e){return Array.isArray(e)?e.filter(t=>t&&typeof t=="object"&&typeof t.id=="string"):[]}function An(e){let t=new Map;for(let n of ye(e.nodes))t.set(n.id,{node:n,config:!1});for(let n of ye(e.configs))t.set(n.id,{node:n,config:!0});return t}function xt(e){let t={};for(let n of ni)e[n]!==void 0&&(t[n]=e[n]);return t}function hr(e,t){let n=new Set([...Object.keys(e??{}),...Object.keys(t??{})]),r=[];for(let o of n)vf.has(o)||mr(e?.[o])!==mr(t?.[o])&&r.push(o);return r.sort()}function sd(e){return e.length>0&&e.every(t=>ri.has(t))}function id(e,t){let n={...e};for(let r of ri)t[r]===void 0?delete n[r]:n[r]=t[r];return n}function At(e){if(!e)return{};let t={};return typeof e.type=="string"&&(t.nodeType=e.type),typeof e.name=="string"&&e.name&&(t.nodeName=e.name.slice(0,120)),t}function ld(e,t){let n=e.find(r=>r.id===t&&(r.type==="tab"||r.type==="subflow"));if(n)return{tab:n,nodes:e.filter(r=>r.id!==t&&r.z===t)}}function Wt(e,t,n){let r=ld(e,t);if(!r)return;let o=new Set(ye(n?.configs).map(c=>c.id)),s=new Set(ye(n?.nodes).map(c=>c.id)),i=[],d=[];for(let c of r.nodes)((o.has(c.id)?!0:s.has(c.id)?!1:c.type!=="group"&&c.x===void 0&&c.y===void 0&&c.wires===void 0)?d:i).push(c);let l={id:t,type:r.tab.type,...xt(r.tab),nodes:i};return d.length>0&&(l.configs=d),l}function bo(e,t){if(typeof e=="string")return e.includes(J)&&(typeof t!="string"||ed(e,t));if(Array.isArray(e)){let n=Array.isArray(t)?t:[],r=new Set;return e.some((o,s)=>{let i=vt(o),d=i===void 0?s:n.findIndex((l,c)=>!r.has(c)&&vt(l)===i);return r.add(d),bo(o,n[d])})}if(e&&typeof e=="object"){let n=t&&typeof t=="object"?t:{};return Object.entries(e).some(([r,o])=>bo(o,n[r]))}return!1}function cd(e){let{flowId:t,proposed:n,liveFlows:r,liveRev:o}=e,s=[],i=a(w=>{s.length<kf&&s.push(w)},"push"),d=ld(r,t);if(!d)return i({kind:"tab_deleted",nodeId:t,message:"The flow tab this proposal edits was deleted in the editor.",choices:[]}),{state:"conflict",rev:o,conflicts:s,liveChangedIds:[t]};let l=e.base??(e.baseRev===o?Wt(r,t,n):void 0);if(l===void 0)return i({kind:"base_unavailable",nodeId:t,message:"This proposal was stored before base snapshots existed and the flows changed since, so live edits cannot be told apart from the proposal. Ask the AI to resubmit, or apply the proposal over the live tab.",choices:["proposed"]}),{state:"conflict",rev:o,conflicts:s,liveChangedIds:[]};let c=Wt(r,t,n),u=An(l),p=An(c),f=An(n),h=[],m=xt(l),g=xt(d.tab),y=xt(n),A=hr(m,g),x=hr(m,y),$=y;A.length>0&&(h.push(t),x.length===0?$=g:i({kind:"tab_changed_both",nodeId:t,fields:[...new Set([...A,...x])].sort(),message:"Both the editor and the proposal changed the flow tab settings.",choices:["live","proposed"]})),$={...$};for(let w of ni)g[w]!==void 0&&$[w]===void 0&&($[w]=w==="env"?[]:dd[w]);let L=new Map,D=new Set([...u.keys(),...p.keys(),...f.keys()]),H=new Map;for(let w of r)w.id!==t&&w.z!==t&&H.set(w.id,w);for(let w of D){let F=u.get(w),I=p.get(w),C=f.get(w),ee=F&&I?hr(F.node,I.node):[],ot=F&&C?hr(F.node,C.node):[];(F&&(!I||ee.length>0)||!F&&I)&&h.push(w),C&&F&&H.has(w)?i({kind:"id_collision",nodeId:w,...At(C.node),message:"This node id now exists outside this flow tab.",choices:["live"]}):F&&I&&C?ot.length===0?L.set(w,I):ee.length===0?L.set(w,C):sd(ee)?L.set(w,{...C,node:id(C.node,I.node)}):sd(ot)&&!Xa(F.node,I.node)?L.set(w,{...I,node:id(I.node,C.node)}):i({kind:"node_changed_both",nodeId:w,...At(I.node),fields:[...new Set([...ee,...ot])].filter(Un=>!ri.has(Un)).sort(),message:"Both the editor and the proposal changed this node.",choices:["live","proposed"]}):F&&I&&!C?ee.length===0?L.set(w,null):i({kind:"node_deleted_in_proposal_modified_live",nodeId:w,...At(I.node),fields:ee,message:"The proposal deletes this node, but it was changed in the editor.",choices:["live","proposed"]}):F&&!I&&C?ot.length===0?L.set(w,null):i({kind:"node_deleted_live",nodeId:w,...At(C.node),fields:ot,message:"This node was deleted in the editor, but the proposal changes it.",choices:["live","proposed"]}):F&&!I&&!C?L.set(w,null):!F&&I&&C?i({kind:"id_collision",nodeId:w,...At(C.node),fields:hr(I.node,C.node),message:"The proposal adds a node whose id was also added in the editor.",choices:["live","proposed"]}):!F&&I&&!C?L.set(w,I):!F&&!I&&C&&(H.get(w)??(w===t?d.tab:void 0)?i({kind:"id_collision",nodeId:w,...At(C.node),message:"The proposal adds a node whose id now exists on another flow tab.",choices:["live"]}):L.set(w,C))}if(s.length>0)return{state:"conflict",rev:o,conflicts:s,liveChangedIds:ei(h)};let T=[],k=[],_=new Set,E=a(w=>{if(_.has(w))return;let F=L.get(w);_.add(w),F&&(F.config?k:T).push(ud(F.node))},"emit");for(let w of ye(n.nodes))E(w.id);for(let w of ye(n.configs))E(w.id);for(let w of ye(c.nodes))E(w.id);for(let w of ye(c.configs))E(w.id);let v={id:t,type:d.tab.type,...$,nodes:T};(k.length>0||Array.isArray(n.configs))&&(v.configs=k);let S=new Set([...T,...k].map(w=>w.id)),N=new Set(r.filter(w=>w.id!==t&&w.z!==t).map(w=>w.id));for(let w of[...T,...k]){let F=[];if(Array.isArray(w.wires)){for(let C of w.wires)if(Array.isArray(C))for(let ee of C)typeof ee=="string"&&!S.has(ee)&&F.push(ee)}if(typeof w.g=="string"&&w.g.length>0&&!S.has(w.g)&&F.push(w.g),w.type==="group"&&Array.isArray(w.nodes))for(let C of w.nodes)typeof C=="string"&&!S.has(C)&&F.push(C);if(Array.isArray(w.links))for(let C of w.links)typeof C=="string"&&!S.has(C)&&!N.has(C)&&F.push(C);let I=[...new Set(F)].sort();I.length>0&&i({kind:"dangling_reference",nodeId:w.id,...At(w),fields:I,message:"After merging, this node points to a node that no longer exists (deleted in the editor).",choices:["live"]}),bo(w,p.get(w.id)?.node)&&i({kind:"redacted_placeholder",nodeId:w.id,...At(w),message:"A [REDACTED] placeholder would be deployed over a real value. Ask the AI to resubmit.",choices:[]})}if(d.tab.type==="subflow"){let w=new Set;for(let F of[d.tab.in,d.tab.out])if(Array.isArray(F)){for(let I of F)if(Array.isArray(I?.wires))for(let C of I.wires)typeof C?.id=="string"&&!S.has(C.id)&&w.add(C.id)}w.size>0&&i({kind:"dangling_reference",nodeId:t,fields:[...w].sort(),message:"A subflow port points to a node that would no longer exist.",choices:[]})}return bo($,g)&&i({kind:"redacted_placeholder",nodeId:t,message:"A [REDACTED] placeholder would be deployed into the tab settings. Ask the AI to resubmit.",choices:[]}),s.length>0?{state:"conflict",rev:o,conflicts:s,liveChangedIds:ei(h)}:Sf(v,n,o,[],ei(h))}function Sf(e,t,n,r,o){let s=gr(e);return{state:s===gr(t)?"clean":"merged",rev:n,conflicts:r,merged:e,mergedHash:s,liveChangedIds:o}}function gr(e){return Af(xf(e))}function xf(e){let t=[...ye(e.nodes),...ye(e.configs)].map(ud).sort((n,r)=>n.id<r.id?-1:n.id>r.id?1:0);return{...dd,...xt(e),nodes:t}}function ud(e){if(!("z"in e))return e;let t={...e};return delete t.z,t}function ei(e){return[...new Set(e)]}function pd(e){let{flowId:t,nodeId:n,choice:r,liveFlows:o}=e,s=[],i=Wt(o,t,e.proposed);if(!i)throw new Error("flow tab deleted");if(e.base===void 0){if(r!=="proposed")throw new Error("invalid choice");return{base:St(i),proposed:St(e.proposed),warnings:s}}let d=St(e.base),l=St(e.proposed);if(r==="proposed"&&(s.push(...fr(d,l).filter(h=>h.nodeId===n)),s.push(...fr(i,l).filter(h=>h.nodeId===n&&!s.some(m=>m.nodeId===h.nodeId)))),n===t){let h=xt(i),m=r==="proposed"?Qs({id:t,type:i.type??"tab",...xt(d)},{id:t,type:i.type??"tab",...xt(l)},{id:t,type:i.type??"tab",...h},s):h;for(let g of ni)delete d[g],h[g]!==void 0&&(d[g]=St(h[g])),delete l[g],m[g]!==void 0&&(l[g]=St(m[g]));return{base:d,proposed:l,warnings:s}}let u=An(i).get(n),p=An(d).get(n),f=An(l).get(n);return r==="proposed"&&p&&f&&u&&ti(l,n,{...f,node:Qs(p.node,f.node,u.node,s)}),ti(d,n,u?{...u,node:St(u.node)}:void 0),r==="live"&&ti(l,n,u?{...u,node:St(u.node)}:void 0),{base:d,proposed:l,warnings:s}}function ti(e,t,n){let r=ye(e.nodes),o=ye(e.configs),s=o.some(l=>l.id===t),i=r.some(l=>l.id===t);if(e.nodes=r.filter(l=>l.id!==t),Array.isArray(e.configs)&&(e.configs=o.filter(l=>l.id!==t)),!n)return;s||!i&&n.config?e.configs=[...ye(e.configs),n.node]:e.nodes=[...e.nodes,n.node]}function St(e){return e===void 0?e:JSON.parse(JSON.stringify(e))}var ad,ni,ri,dd,vf,kf,yr=q(()=>{"use strict";ad=require("crypto");un();Pe();ni=["label","disabled","info","env"],ri=new Set(["x","y"]),dd={label:"",disabled:!1,info:"",env:[]},vf=new Set(["z"]),kf=200;a(mr,"stableStringify");a(Af,"hashTab");a(ye,"asNodes");a(An,"mapTab");a(xt,"tabProps");a(hr,"changedFields");a(sd,"positionOnly");a(id,"withPosition");a(At,"nodeLabel");a(ld,"liveTabFromFlows");a(Wt,"nestedLiveTab");a(bo,"containsNewPlaceholder");a(cd,"rebaseFlowTab");a(Sf,"finish");a(gr,"hashFlowTab");a(xf,"normalizeForHash");a(ud,"withoutZ");a(ei,"dedupe");a(pd,"applyConflictChoice");a(ti,"setNode");a(St,"clone")});function gd(e,t){md=e,vo=t??(()=>Date.now())}function yd(){let e=md();return(Number.isFinite(e)&&e>=1?e:fd)*hd}function Tf(e){return`Proposal expired: it stayed open longer than the proposal TTL (${e} day${e===1?"":"s"}). Ask the AI to resubmit if still needed.`}function ko(e,t=vo()){let n=Date.parse(e.createdAt);return Number.isFinite(n)&&t-n>yd()}function ne(e,t={}){let n=vo(),r=Math.round(yd()/hd),o=0;for(let s of e.list())if(U(s.status)&&!(t.flowId&&s.payload.flowId!==t.flowId)&&!e.isApproving(s.id)&&ko(s,n))try{e.markClosed(s.id,"expired",Tf(r)),o+=1}catch{}return o}function oi(e,t){return cd({flowId:e.payload.flowId,base:e.baseFlow,proposed:e.payload.flow,liveFlows:t.flows,liveRev:t.rev,baseRev:e.expectedRev})}function si(e,t=vo()){let n={rev:e.rev,state:e.state,conflicts:e.conflicts.map(r=>({...r,choices:[...r.choices]})),liveChangedCount:e.liveChangedIds.length,checkedAt:new Date(t).toISOString()};return e.mergedHash&&(n.mergedHash=e.mergedHash),n}function Ze(e,t,n,r={}){let o=oi(t,n);return{proposal:e.isApproving(t.id)&&!r.duringApprove?e.get(t.id):e.recordRebase(t.id,si(o)),result:o}}async function wr(e,t,n={}){let r=ne(e,n.flowId?{flowId:n.flowId}:{}),o=n.live;if(!o&&t)try{o=await Oe(t).getFlows()}catch{}if(!o)return{expired:r,conflicts:0,checked:0};let s=$e(e,o,n);return{...s,expired:r+s.expired}}function $e(e,t,n={}){let r={expired:0,conflicts:0,checked:0};for(let o of e.list())if(U(o.status)&&!(n.flowId&&o.payload.flowId!==n.flowId)&&!e.isApproving(o.id))try{if(ko(o)){r.expired+=ne(e,{flowId:o.payload.flowId});continue}let{proposal:s}=Ze(e,o,t);r.checked+=1,s.status==="conflict"&&(r.conflicts+=1)}catch{}return r}function wd(e,t=500){let n,r,o=!1,s=a(async()=>{if(r)return o=!0,r;r=(async()=>{do{o=!1;try{await e()}catch{}}while(o)})();try{await r}finally{r=void 0}},"exec");return{trigger(){n&&clearTimeout(n),n=setTimeout(()=>{n=void 0,s()},t),n.unref?.()},async flush(){n&&(clearTimeout(n),n=void 0),await s()},cancel(){n&&clearTimeout(n),n=void 0}}}async function Qe(e){if(e)try{return await Oe(e).getFlows()}catch{return}}var fd,hd,md,vo,et=q(()=>{"use strict";Je();yr();Pe();fd=7,hd=864e5,md=a(()=>fd,"ttlDaysProvider"),vo=a(()=>Date.now(),"nowProvider");a(gd,"configureProposalTtl");a(yd,"proposalTtlMs");a(Tf,"ttlExpiredReason");a(ko,"isPastTtl");a(ne,"expireByTtl");a(oi,"rebaseProposal");a(si,"rebaseInfoFrom");a(Ze,"syncOneProposal");a(wr,"syncOpenProposals");a($e,"rebaseOpenProposalsWith");a(wd,"createDebouncedSync");a(Qe,"tryRawLive")});var Rd={};Ki(Rd,{applyFlowNodeOps:()=>Gt,applyReplaceTextToValue:()=>Td,buildPatchedFlowTab:()=>br,collectSetProps:()=>ii,decodeBase64UrlJson:()=>vr,findTextOccurrences:()=>xd,interpretPatch:()=>kr});function Sd(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function Rf(e){if(typeof e!="object"||e===null||Array.isArray(e))throw new b(`add op needs node as an object (got ${Sd(e)})`,"invalid_node")}async function br(e,t,n){return Gt(e,t,n.map(r=>({op:"set",nodeId:r.nodeId,set:r.set})))}function xd(e,t){if(!t)return[];let n=[],r=0;for(;r<=e.length;){let o=e.indexOf(t,r);if(o<0)break;n.push(o),r=o+t.length}return n}function Td(e,t,n,r,o){if(Ad.has(t))throw new b(`Cannot change protected node property '${t}' via replace_text`,"protected_property");if(typeof n!="string"||n.length===0)throw new b("find must be a non-empty string","missing_find");if(typeof r!="string")throw new b("replace must be a string","invalid_replace");if(e===void 0)throw new b(`Field '${t}' was not found on the node`,"field_not_found");if(typeof e!="string")throw new b(`Field '${t}' is not a string (got ${e===null?"null":typeof e})`,"field_not_string");let s=xd(e,n);if(s.length===0)throw new b(`Text not found in field '${t}'`,"text_not_found",{details:{matchCount:0}});let i=o;if(i===void 0){if(s.length>1)throw new b(`Found ${s.length} matches in '${t}'; pass occurrence (1-based) to pick one`,"ambiguous_match",{statusCode:409,details:{matchCount:s.length}});i=1}if(!Number.isInteger(i)||i<1||i>s.length)throw new b(`occurrence must be an integer from 1 to ${s.length}`,"invalid_occurrence",{details:{matchCount:s.length}});let d=s[i-1];if(d===void 0)throw new b(`occurrence must be an integer from 1 to ${s.length}`,"invalid_occurrence",{details:{matchCount:s.length}});return e.slice(0,d)+r+e.slice(d+n.length)}function Pf(e){let t=[];for(let n of e)if(n.op==="add"){let r=n.node;if(typeof r!="object"||r===null||Array.isArray(r)){t.push({code:"invalid_node",error:`add op needs node as an object (got ${Sd(r)})`});continue}let o=r;(!o.id||typeof o.id!="string")&&t.push({code:"missing_node_id",error:"add op needs node.id"});try{yt(o.type,"node.type")}catch(s){s instanceof b&&t.push({code:s.code,error:s.message,nodeId:o.id})}"wires"in o&&wn(o.wires,t,typeof o.id=="string"?o.id:void 0)}else n.op==="set"&&typeof n.set=="object"&&"wires"in n.set&&wn(n.set.wires,t,n.nodeId);return t}function _f(e){let t=new Set;for(let n of e)n.op==="add"&&typeof n.node.id=="string"?t.add(n.node.id):n.op!=="add"&&typeof n.nodeId=="string"&&t.add(n.nodeId);return t}async function Gt(e,t,n,r={}){if(!t)throw new b("flow id is required","missing_flow_id");if(!n.length)throw new b("At least one node edit is required","missing_edits");let o=Pf(n);o.push(...Os([n.flatMap(m=>m.op==="add"?[m.node]:[])],"the batch (add ops)")),at(o);let s=await e.getFlow(t);if(r.expectedRev!==void 0&&s.rev!==r.expectedRev){let m=Array.isArray(s.flow.nodes)?[...s.flow.nodes]:[];return{expectedRev:s.rev,flow:{...s.flow,id:s.flow.id,type:s.flow.type??"tab",nodes:m}}}let i=Array.isArray(s.flow.nodes)?[...s.flow.nodes]:[],d=Array.isArray(s.flow.configs)?[...s.flow.configs]:[],l={...s.flow,id:s.flow.id,type:s.flow.type??"tab",nodes:i,configs:d},c=i.map(m=>({...m})),u=[];for(let m of n)try{m.op==="add"&&r.nodeTypes&&await Ca(r.nodeTypes,m.node.type),vd(c,t,m)}catch(g){if(g instanceof b)u.push({code:g.code,error:g.message,...g.statusCode!==400?{statusCode:g.statusCode}:{},...g.details??{}});else throw g}u.push(...Ms(c,t));let p=_f(n),f=await $s(c,r.nodeTypes,{softWarningNodeIds:p});u.push(...f.errors),at(u);for(let m of n)vd(i,t,m);let h=[...f.warnings];if(r.nodeTypes){let m=await Ds(i,d,r.nodeTypes);h.push(...m.filter(g=>!g.nodeId||p.has(g.nodeId)))}return{expectedRev:s.rev,flow:l,...h.length>0?{warnings:h}:{}}}function bd(e,t,n){if(!t||typeof t!="string")throw new b("Each edit needs a node id","missing_node_id");let r=e.findIndex(o=>o.id===t);if(r<0||!e[r])throw new b(`Node '${t}' was not found in flow '${n}'`,"node_not_found",{statusCode:404});return r}function vd(e,t,n){if(n.op==="add"){let i=n.node;if(!i.id||typeof i.id!="string")throw new b("add op needs node.id","missing_node_id");if(yt(i.type,"node.type"),e.some(d=>d.id===i.id))throw new b(`Node '${i.id}' already exists in flow '${t}'`,"node_already_exists");e.push({...i,id:i.id,type:i.type,z:typeof i.z=="string"&&i.z?i.z:t});return}if(n.op==="remove"){let i=bd(e,n.nodeId,t);e.splice(i,1);return}let r=bd(e,n.nodeId,t),o=e[r];if(!o)throw new b(`Node '${n.nodeId}' was not found in flow '${t}'`,"node_not_found",{statusCode:404});if(n.op==="replace_text"){if(!(n.field in o))throw new b(`Field '${n.field}' was not found on the node`,"field_not_found");let i=Td(o[n.field],n.field,n.find,n.replace,n.occurrence);e[r]={...o,id:o.id,type:o.type,[n.field]:i};return}let s={...o,id:o.id,type:o.type};for(let[i,d]of Object.entries(n.set)){if(Ad.has(i))throw new b(`Cannot change protected node property '${i}' via GET propose`,"protected_property");if(i==="wires"){let l=[];wn(d,l,n.nodeId),at(l)}s[i]=d}e[r]=s}function kd(e){if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean"||typeof e=="bigint")return String(e)}function ii(e){let t={},n=e.set;if(n&&typeof n=="object"&&!Array.isArray(n))for(let[r,o]of Object.entries(n)){let s=kd(o);s!==void 0&&(t[r]=s)}for(let[r,o]of Object.entries(e)){if(!r.startsWith("set.")||r.length<=4)continue;let s=r.slice(4);if(typeof o=="string")t[s]=o;else if(Array.isArray(o)&&typeof o[0]=="string")t[s]=o[0];else{let i=kd(o);i!==void 0&&(t[s]=i)}}return t}function vr(e){let t;try{t=Buffer.from(e,"base64url").toString("utf8")}catch{throw new b("patch must be base64url-encoded JSON","invalid_patch")}try{return JSON.parse(t)}catch{throw new b("patch JSON is malformed","invalid_patch_json")}}function Ef(e,t){if(typeof e.flowId=="string"&&e.flowId)return e.flowId;let n=e.flow;if(n&&typeof n=="object"&&!Array.isArray(n)){let r=n.id;if(typeof r=="string"&&r)return r}return t}function ai(e){return typeof e.id=="string"&&e.id?e.id:typeof e.nodeId=="string"&&e.nodeId?e.nodeId:typeof e.node=="string"&&e.node?e.node:""}function Cf(e){if(e==null||e==="")return;let t;if(typeof e=="number")t=e;else if(typeof e=="string")t=Number(e);else if(typeof e=="boolean"||typeof e=="bigint")t=Number(e);else throw new b("occurrence must be a positive integer","invalid_occurrence");if(!Number.isFinite(t))throw new b("occurrence must be a positive integer","invalid_occurrence");return Math.trunc(t)}function If(e){let t=e.node;if(t!==void 0)return Rf(t),{op:"add",node:t};let n={...e};return delete n.op,{op:"add",node:n}}function Lf(e){let t=ai(e);if(!t)throw new b("replace_text op needs node id","missing_node_id");let n=typeof e.field=="string"?e.field:"";if(!n)throw new b("replace_text op needs field","missing_field");if(typeof e.find!="string")throw new b("replace_text op needs find string","missing_find");if(typeof e.replace!="string")throw new b("replace_text op needs replace string","invalid_replace");let r=Cf(e.occurrence),o={op:"replace_text",nodeId:t,field:n,find:e.find,replace:e.replace};return r!==void 0&&(o.occurrence=r),o}function Nf(e){let t=ai(e);if(!t)throw new b("Each patch.nodes entry needs id","missing_node_id");let n=e.set,r={};if(n&&typeof n=="object"&&!Array.isArray(n))Object.assign(r,n);else for(let[o,s]of Object.entries(e))o==="id"||o==="nodeId"||o==="node"||o==="set"||o==="op"||(r[o]=s);return{op:"set",nodeId:t,set:r}}function Ff(e){let t=typeof e.op=="string"?e.op.toLowerCase():"set";if(t==="add")return If(e);if(t==="remove"){let n=ai(e);if(!n)throw new b("remove op needs id","missing_node_id");return{op:"remove",nodeId:n}}if(t==="replace_text")return Lf(e);if(t!=="set")throw new b("nodes[].op must be 'add', 'remove', 'set', or 'replace_text'","invalid_patch");return Nf(e)}function kr(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new b("patch must be a JSON object","invalid_patch");let n=e;if(n.type==="replace_flow"||n.flow&&typeof n.flow=="object"){let r=Ef(n,t);if(!r)throw new b("patch.replace_flow needs flowId","missing_flow_id");return{kind:"replace_flow",flowId:r,flow:n.flow}}if(Array.isArray(n.nodes)){let r=typeof n.flowId=="string"?n.flowId:t;if(!r)throw new b("patch.nodes needs flow id","missing_flow_id");let o=[];for(let i of n.nodes){if(!i||typeof i!="object"||Array.isArray(i))throw new b("Each patch.nodes entry must be an object","invalid_patch");o.push(Ff(i))}let s=o.filter(i=>i.op==="set").map(i=>({nodeId:i.nodeId,set:i.set}));return{kind:"nodes",flowId:r,ops:o,edits:s}}throw new b("patch must include nodes[] edits or type/flow for replace_flow","invalid_patch")}var Ad,Ao=q(()=>{"use strict";ie();Hs();qt();Ad=new Set(["id","type","z"]);a(Sd,"describeNonObjectNode");a(Rf,"assertAddNodeObject");a(br,"buildPatchedFlowTab");a(xd,"findTextOccurrences");a(Td,"applyReplaceTextToValue");a(Pf,"collectOpShapeIssues");a(_f,"touchedNodeIdsFromOps");a(Gt,"applyFlowNodeOps");a(bd,"requireNodeIndex");a(vd,"applyOneNodeOp");a(kd,"stringifyQueryValue");a(ii,"collectSetProps");a(vr,"decodeBase64UrlJson");a(Ef,"resolveReplaceFlowId");a(ai,"resolveNodeId");a(Cf,"parseOccurrence");a(If,"parseAddOp");a(Lf,"parseReplaceTextOp");a(Nf,"parseSetOp");a(Ff,"parseNodeOp");a(kr,"interpretPatch")});function Kt(e,t){return Ut(e.createdBy)===t.id}function di(e,t){return!t||t.length===0||t.includes(e)}function Of(e){return typeof e=="string"&&_d.has(e)}function xn(e,t){let n=$f[e],r=e!=="status"&&Array.isArray(t);return new b(r?`Invalid ${e} filter: pass it at most once (multiple values are not supported). Allowed: ${n}`:`Invalid ${e} filter. Allowed: ${n}`,"invalid_filter",{details:{parameter:e,allowed:n,hint:`Pass ${e} as ${n}, or omit it.`}})}function So(e,t){if(Array.isArray(t))throw xn(e,t);return t}function Mf(e){return typeof e=="number"?Number.isInteger(e)?e:null:typeof e=="string"&&/^-?\d+$/.test(e.trim())?Number(e.trim()):null}function li(e){return e==null||e===""}function Df(e){if(!li(e)){if(e==="true"||e==="1"||e===!0)return!0;if(e==="false"||e==="0"||e===!1)return!1;throw xn("mine",e)}}function ci(e){if(li(e))return;let t=(Array.isArray(e)?e:[e]).flatMap(r=>typeof r=="string"?r.split(","):[r]),n=[];for(let r of t){let o=typeof r=="string"?r.trim():r;if(o!==""){if(!Of(o))throw xn("status",e);n.includes(o)||n.push(o)}}return n.length>0?n:void 0}function Pd(e,t,n,r){if(li(t))return;let o=Mf(t);if(o===null||o<n||o>r)throw xn(e,t);return o}function xo(e){let t={},n=Df(So("mine",e.mine));n!==void 0&&(t.mine=n);let r=ci(e.status);r&&(t.status=r);let o=So("flowId",e.flowId??e.flow_id??e.flow);typeof o=="string"&&o.length>0&&(t.flowId=o);let s=Pd("limit",So("limit",e.limit),1,Sn);s!==void 0&&(t.limit=s);let i=Pd("offset",So("offset",e.offset),0,Number.MAX_SAFE_INTEGER);return i!==void 0&&(t.offset=i),t}function Jt(e){let t=e.offset+e.returned,n=t<e.total;return{total:e.total,offset:e.offset,...e.limit!==null?{limit:e.limit}:{},hasMore:n,...n?{nextOffset:t}:{}}}function Hf(e,t){let n=e.length,r=t.offset!==void 0&&t.offset>0?t.offset:0;if(t.limit===void 0)return{items:r>0?e.slice(r):e,total:n,limit:null,offset:r};let o=Math.min(Math.max(1,t.limit),Sn);return{items:e.slice(r,r+o),total:n,limit:o,offset:r}}function Tn(e,t,n={},r={}){let o=r.mine===!0?t.id:void 0,s={};o&&(s.linkId=o),r.flowId&&(s.flowId=r.flowId);let d=e.list(s).filter(c=>r.mine!==!1||!Kt(c,t)).filter(c=>di(c.status,r.status)).map(c=>({...pe(lt(c),n),mine:Kt(c,t)})),l=Hf(d,r);return{proposals:l.items,total:l.total,limit:l.limit,offset:l.offset}}function ct(e,t,n,r={}){if(!n)throw new b("Missing proposal id","missing_proposal_id");let o=e.get(n);return{...pe(lt(o),r),mine:Kt(o,t)}}function jf(e){let t={kind:"access-link",linkId:e.id};return typeof e.label=="string"&&e.label&&(t.label=e.label),t}function Rn(e,t,n,r,o,s={}){if(!r)throw new b("Missing proposal id","missing_proposal_id");let i;try{i=e.get(r)}catch(f){throw f instanceof me,f}if(!Kt(i,n))throw new jt(r);if(!U(i.status))throw new Y(r,i.status);let d=typeof o=="string"&&o.trim()?o.replace(/\0/g,"").trim().slice(0,2e3):"Withdrawn by access link",l=jf(n),c=e.markWithdrawn(r,l,d),u=pe(lt(c),s),p=u.flowLabel||void 0;return t.append("proposal.withdrawn",l,{proposalId:c.id,flowId:c.payload.flowId,title:u.title,displayName:u.displayName,reason:d,...p?{flowLabel:p}:{}}),{...u,mine:!0}}function Uf(e,t,n,r={}){let o;try{o=e.get(t.id)}catch{return{}}if(r.live){$e(e,r.live,{flowId:o.payload.flowId});try{o=e.get(t.id)}catch{return{}}}return U(o.status)?Ye(Ve(e,o,n)):{}}function he(e,t,n,r={},o={}){let s="mine"in n?n:{...pe(n,r),mine:Kt(n,t)},i=0;if(o.live)try{let c=e.get(s.id).payload.flowId;i=$e(e,o.live,{flowId:c}).expired;let u=e.get(s.id);s={...pe(u,r),mine:Kt(u,t)}}catch{}let d=fe(lt(s),r),l={...d,mine:s.mine,...ue(i)};return o.includeConflicts!==!1&&U(d.status)&&Object.assign(l,Uf(e,d,t.id)),l}async function ut(e){let t=await e.getFlows();return{rev:t.rev,flows:t.flows}}async function Me(e,t){if(!t)throw new b("Missing flow id","missing_flow_id");let n=await e.getFlow(t);return{rev:n.rev,flow:yn(t,n.flow)}}function Bf(e){if(typeof e.expectedRev=="string"&&e.expectedRev.length>0)return e.expectedRev;if(typeof e.expected_rev=="string"&&e.expected_rev.length>0)return e.expected_rev}function Ed(e,t){if("nodes"in e&&e.nodes!==void 0&&!Array.isArray(e.nodes))throw new b("payload.nodes must be an array of node ops","invalid_nodes");if(t!==void 0&&!Array.isArray(t))throw new b("nodes must be an array of node ops","invalid_nodes")}async function qf(e,t,n,r,o){let{applyFlowNodeOps:s,interpretPatch:i}=await Promise.resolve().then(()=>(Ao(),Rd));Ed(n,r);let d=Array.isArray(n.nodes)?n.nodes:Array.isArray(r)?r:null;if(!d)throw new b("payload.flow is required","missing_flow");let l=n.flowId,c=typeof l=="string"?l:"";if(!c)throw new b("payload.flowId is required","missing_flow_id");let u=i({nodes:d,flowId:c},c);if(u.kind!=="nodes")throw new b("payload.nodes must be node ops","invalid_patch");let p=await s(e,u.flowId,u.ops,{expectedRev:t,...o?{nodeTypes:o}:{}});if(p.expectedRev!==t)throw new te(t,p.expectedRev,"submit_proposal");return{flowId:u.flowId,flow:p.flow}}async function zf(e,t,n,r,o){let s=n.flowId,i=n.flow,d=!i||typeof i!="object"||Array.isArray(i);if(d&&Ed(n,r),d&&(Array.isArray(n.nodes)||Array.isArray(r))){let l=await qf(e,t,n,r,o);s=l.flowId,i=l.flow}if(typeof s!="string"||s.length===0)throw new b("payload.flowId is required","missing_flow_id");if(!i||typeof i!="object"||Array.isArray(i))throw new b("payload.flow is required","missing_flow");return{flowId:s,flow:i}}function Wf(e,t){try{return{safeExpectedRev:We(e.slice(0,200),"expectedRev"),safeFlowId:We(t.slice(0,200),"flowId")}}catch{throw new b("expectedRev and payload.flowId must be safe path segments","invalid_path_segment")}}function Gf(e,t){let n=nd(it(e),t);return n.unresolved.length>0&&at(n.unresolved.map(r=>({code:"redacted_placeholder_mismatch",error:`Node ${r.nodeId} field ${r.property}: ${r.reason}. [REDACTED] placeholders are restored only when left exactly where they were shown (same node, field, env/header name, and unchanged line). Do not copy, move, or add placeholders; keep those lines unchanged or supply a real new value.`,nodeId:r.nodeId,property:r.property,statusCode:400}))),n}async function Cd(e,t,n){let r=e.type,o=Bf(e),s=e.payload;if(r!=="replace_flow")throw new b("type must be 'replace_flow'","invalid_proposal_type");if(!o)throw new b("expectedRev is required (top-level field next to type and payload)","missing_expected_rev");if(!s||typeof s!="object"||Array.isArray(s))throw new b("payload must be an object","invalid_payload");let i=s,d=Oe(t),l=await d.getFlows(),c=so(l,d.kind);if(l.rev!==o){let T=Ts(d)?.get(o);if(!T)throw new te(o,l.rev,"submit_proposal");c=so(T,d.kind)}let u=await zf(kt(c),o,i,e.nodes,n),{safeExpectedRev:p,safeFlowId:f}=Wf(o,u.flowId),h=u.flow,m=Buffer.byteLength(JSON.stringify(s),"utf8");if(m>K)throw new le("proposal payload",m,K);at(Na(h,f));let y=await c.getFlows();if(y.rev!==p)throw new te(p,y.rev,"submit_proposal");let A=Gf(h,y.flows),x=it(A.flow);at(La(x,f,y.flows));let $=await c.getFlow(f);if($.rev!==p)throw new te(p,$.rev,"submit_proposal");let L=it($.flow);n&&await Ea(n,x);let{errors:D,warnings:H}=await Fa(x,{flowId:f,baseFlow:L,...n?{nodeTypes:n}:{}});return at(D),H.unshift(...fr(L,x).map(T=>({...T})),...od(A.restoredFields,x,y.flows).map(T=>({...T}))),{safeExpectedRev:p,safeFlowId:f,safeFlow:x,safeBaseFlow:L,restoration:A,flowWarnings:H,head:l}}function Id(e){let t={kind:"access-link",linkId:e.id};return e.label!==void 0&&(t.label=e.label),t}async function Tt(e,t,n,r,o,s={},i){let d=await Cd(r,o,i),{safeExpectedRev:l,safeFlowId:c,safeFlow:u,safeBaseFlow:p,restoration:f,flowWarnings:h}=d,m=Id(n),g=n.oauthClientId&&n.oauthUser?{kind:"oauth",clientId:n.oauthClientId,user:n.oauthUser}:m,y=ar(r.title)??Bs(p,u),A=e.create({type:"replace_flow",expectedRev:l,payload:{flowId:c,flow:u},baseFlow:p,...f.restoredFields.length>0?{restoredSecrets:f.restoredFields}:{},title:y,createdBy:g,...h.length>0?{warnings:h}:{}}),x=Ze(e,A,d.head).proposal,$=pe(lt(x),s),L=$.flowLabel||void 0;return t.append("proposal.created",m,{proposalId:x.id,flowId:c,expectedRev:l,...d.head.rev!==l?{liveRev:d.head.rev}:{},...x.rebase?{rebaseState:x.rebase.state}:{},title:$.title,displayName:$.displayName,...L?{flowLabel:L}:{},...h.length>0?{warningCount:h.length}:{},...f.restored>0?{restoredRedactions:f.restored}:{}}),$}async function To(e,t,n,r,o,s,i={},d){if(!r)throw new b("Missing proposal id","missing_proposal_id");let l=e.get(r);if(!Kt(l,n))throw new jt(r);if(!U(l.status))throw new Y(r,l.status);let c=await Cd({type:"replace_flow",...o,payload:o.payload??{flowId:l.payload.flowId}},s,d);if(c.safeFlowId!==l.payload.flowId)throw new b(`payload.flowId must stay '${l.payload.flowId}' (submit a new proposal for another flow)`,"flow_id_mismatch");let u=ar(o.title),p=e.updateBody(r,{baseFlow:c.safeBaseFlow,flow:c.safeFlow,expectedRev:c.safeExpectedRev,restoredSecrets:c.restoration.restoredFields,warnings:c.flowWarnings,...u?{title:u}:{}}),f=Ze(e,p,c.head).proposal,h=pe(lt(f),i);return t.append("proposal.resolved",Id(n),{proposalId:r,flowId:f.payload.flowId,expectedRev:c.safeExpectedRev,...c.restoration.restored>0?{restoredRedactions:c.restoration.restored}:{},title:h.title,displayName:h.displayName,...f.rebase?{rebaseState:f.rebase.state}:{}}),{...h,mine:!0}}async function Ld(e,t,n,r,o,s,i){if(s!=="live"&&s!=="proposed")throw new b("choice must be 'live' or 'proposed'","invalid_choice");if(typeof o!="string"||o.length===0||o.length>200)throw new b("nodeId is required","missing_node_id");let d=await Oe(i).getFlows(),l=e.get(r);if(!U(l.status))throw new Y(r,l.status);let c=Ze(e,l,d).proposal,u=c.rebase?.conflicts.find(m=>m.nodeId===o);if(!u)throw new b("No current conflict for that node (it may already be resolved). Reload the proposal.","conflict_not_found",{statusCode:409});if(!u.choices.includes(s))throw new b("That choice is not available for this conflict.","invalid_choice");let p;try{p=pd({flowId:c.payload.flowId,base:c.baseFlow,proposed:c.payload.flow,liveFlows:d.flows,nodeId:o,choice:s})}catch(m){throw m instanceof pr?new Ke(r,c.rebase?.conflicts??[]):new b("This conflict cannot be resolved by hand.","invalid_choice")}let f=e.updateBody(r,{baseFlow:it(p.base),flow:it(p.proposed),warnings:[...(c.warnings??[]).filter(m=>m.code!=="secret_may_revert"||m.nodeId!==o),...p.warnings]}),h=Ze(e,f,d).proposal;return t.append("proposal.conflict_resolved",n,{proposalId:r,flowId:h.payload.flowId,nodeId:o,choice:s,kind:u.kind,status:h.status}),h}async function Ar(e,t){let n=Oe(e),r;try{r=(await n.getFlows()).rev}catch{return e}if(r===t)return e;let o=Ts(n)?.get(t);if(!o)return e;let s=so(o,n.kind);return e===n?s:kt(s)}function W(e,t="rest"){return Sr(e,t)}var Sn,_d,$f,b,ie=q(()=>{"use strict";Te();ro();Rs();Je();Zr();se();tt();Hs();qt();zt();Fe();yr();et();Pe();a(Kt,"isMineProposal");Sn=200,_d=new Set(["pending","conflict","conflict","approved","rejected","failed","withdrawn","expired","superseded"]);a(di,"matchesStatusFilter");a(Of,"isProposalStatus");$f={status:[..._d].join(", "),limit:`integer 1\u2013${Sn}`,offset:"integer \u2265 0",mine:"true, false, 1, 0",flowId:"one flow tab id"};a(xn,"invalidFilterError");a(So,"singleQueryValue");a(Mf,"parseIntegerFilter");a(li,"isUnset");a(Df,"parseMineFilter");a(ci,"parseStatusFilter");a(Pd,"parseRangeFilter");a(xo,"parseListProposalsQuery");a(Jt,"listPageFields");a(Hf,"applyListPagination");a(Tn,"listAccessibleProposals");a(ct,"getAccessibleProposal");a(jf,"accessLinkActor");a(Rn,"withdrawMyProposal");a(Uf,"conflictsForProposal");a(he,"presentAccessibleProposalForApi");b=class extends Error{static{a(this,"AiOpsValidationError")}statusCode;code;details;constructor(t,n="validation_error",r={}){super(t),this.name="AiOpsValidationError",this.code=n,this.statusCode=r.statusCode??400,r.details&&(this.details=r.details)}};a(ut,"listFlows");a(Me,"getFlow");a(Bf,"resolveExpectedRev");a(Ed,"assertNodesArrayOrAbsent");a(qf,"materializePayloadFromNodes");a(zf,"resolveReplaceFlowPayload");a(Wf,"assertSafeRevAndFlowId");a(Gf,"strictRestoreOrThrow");a(Cd,"prepareReplaceFlowSubmission");a(Id,"auditActorFor");a(Tt,"submitReplaceFlowProposal");a(To,"resolveMyProposal");a(Ld,"resolveConflictChoice");a(Ar,"adapterAtRev");a(W,"aiOpsErrorToHttp")});function ui(e){return e==="get_link"?86400:2592e3}function pi(e,t){return e==="get_link"?`get_link ttlSeconds must be \u2264 ${t} (24 hours)`:`ttlSeconds must be \u2264 ${t} (30 days) for ${e} tokens`}var Vt,xr=q(()=>{"use strict";a(ui,"maxTtlSecondsForKind");a(pi,"ttlCapErrorMessage");Vt="ttlSeconds must be a positive number of seconds within the cap for the token kind: get_link \u2264 86400 (24h); rest and mcp \u2264 2592000 (30 days)."});function R(e,t,n,r,o=re,s="error"){return{code:e,status:t,meaning:n,hint:r,channels:o,kind:s}}function Tr(e){return Jf.get(e)?.hint}function Nd(e){return e.replace(/\\/g,"\\\\").replace(/\|/g,"\\|")}function Fd(){let e=a(s=>`| \`${s.code}\` | ${s.status} | ${Nd(s.meaning)} | ${Nd(s.hint)} | ${s.channels.join(", ")} |`,"row"),t=["| Code | HTTP | Meaning | Next step | Channels |","| --- | --- | --- | --- | --- |"],n=Po.filter(s=>s.kind==="error"),r=Po.filter(s=>s.kind==="warning"),o=Po.filter(s=>s.kind==="status");return[...t,...n.map(e),"","**Warnings** (in `warnings[]` on a successful propose; never blocking):","",...t,...r.map(e),"","**Proposal statuses.** Open proposals are closed automatically: `expired` after the configured TTL (default 7 days). Revision changes auto-rebase open proposals; `conflict` blocks approval until resolved. Legacy closed proposals stay closed. Submit, get, and list responses include `autoClosedCount` when that request auto-closed proposals (omitted when zero).","",...t,...o.map(e)].join(`
10
- `)}var re,Pn,Ro,Kf,Rt,Po,Jf,_o=q(()=>{"use strict";xr();re=["GET","REST","MCP"],Pn=["GET"],Ro=["REST","MCP"],Kf=["GET","REST"],Rt=["admin"];a(R,"e");Po=[R("duplicate_env_keys",200,"Changed env arrays contain duplicate names; pairing by name is ambiguous when entries are reordered","Review the env values before approval",[...re,"admin"],"warning"),R("approve_queue_timeout",504,"Approval exceeded its queue-wait deadline (default 60 seconds); no deployment was started for this request","Check the live flow and retry approval",Rt),R("deploy_timeout",504,"Deployment exceeded its deadline (default 60 seconds); approvals wait for settlement or the hard cap (default 5 minutes)","Before the cap, late settlement updates the proposal; after the cap, deployPending.queueReleased is true and the outcome stays unknown. Check the live flow before approving again; approval always re-reads live",Rt),R("secret_may_revert",200,"Secret spans could not be aligned during proposed conflict resolution; proposed values were kept","Review the warned field against live flows before approval; an editor secret change may be reverted",[...re,"admin"],"warning"),R("proposal_conflict",409,"Editor and proposal changes conflict (conflicts contains field names only)","Read the current flow and resolve the proposal before approval",[...re,"admin"]),R("rebase_ack_required",409,"The merged result needs review (mergedHash included)","Review the diff and approve with its mergedHash",["admin"]),R("rebase_race",409,"Flows kept changing during approval","Recheck the proposal and retry approval",["admin"]),R("conflict_not_found",409,"The requested conflict no longer exists","Reload the proposal and its conflicts",["admin"]),R("invalid_choice",400,"Invalid manual conflict choice","Use an available live or proposed choice",["admin"]),R("flow_id_mismatch",400,"Resolution tried to change the proposal flow id","Keep the same flowId or submit a new proposal",Ro),R("access_link_missing",401,"No Bearer / path token","Ask the user for a new access link of the right kind"),R("bearer_scheme_required",401,"Auth used Basic (or non-Bearer)","Send `Authorization: Bearer <token>`",Ro),R("token_expired",401,"Known token past `expiresAt` (body includes `expiredAt` and `accessLinksUrl`)","Ask the user to create a new link on the `/ai-access` dashboard \u2192 Access tokens (`accessLinksUrl`)"),R("access_link_invalid",401,"Unknown, revoked, or cleared after restart","Ask the user for a new access link"),R("wrong_token_kind",403,"Token kind does not match this surface","Mint the matching kind on `/ai-access` \u2192 Access tokens; show `tellUser` to the human"),R("ai_features_disabled",403,"Plugin AI features off","Owner enables AI Access / `adminAuth`"),R("auth_required",401,"Admin session required","Sign in to the Node-RED editor",Rt),R("insufficient_permissions",403,"Admin lacks required scope","Need `*` for link mint / approve",Rt),R("invalid_link_kind",400,"Unknown `kind` on create","Use `get_link`, `rest`, or `mcp`",Rt),R("invalid_ttl",400,"`ttlSeconds` is zero, negative, not a number, or over the cap for the kind",Vt,Rt),R("access_link_limit",409,"Too many active links","Revoke an existing link first",Rt),R("access_link_not_found",404,"Unknown link id on revoke","List links and use an existing id",Rt),R("link_create_not_supported",403,"Attempted mint under a GET link","Admin mints via `POST /mcp/api/v1/links` only",Pn),R("rate_limited",429,"Per-IP or per-token limit","Wait `retryAfter` / `Retry-After`"),R("payload_too_large",413,"Body / proposal over ~1.5MB","Shrink content"),R("invalid_json",400,"Malformed JSON body","Fix Content-Type / body",Ro),R("unsupported_media_type",415,"POST body is not `application/json`","Send `Content-Type: application/json`",Ro),R("url_too_long",414,"GET URL over soft cap (~8KB)","Use chunked drafts or REST/MCP",Pn),R("not_found",404,"Unknown path or resource","Re-read docs / discovery"),R("missing_query",400,"Required search `q` missing","Pass `?q=`",Kf),R("invalid_filter",400,"Invalid list filter (`status`, `limit`, `offset`, `mine`); body names `parameter` and `allowed`","Use an allowed value: status one of the proposal statuses; limit integer 1\u2013200; offset integer \u2265 0; mine true|false"),R("invalid_arguments",400,"MCP tool arguments failed schema validation; body names `parameter`","Fix the named argument to match the tool input schema (see tools/list)",["MCP"]),R("invalid_diff_format",400,"Unknown `format` on proposal diff","Use `format=summary` (default) or `format=full`"),R("confirm_required",400,"GET withdraw without `confirm=1`","Repeat the request with `confirm=1`",Pn),R("missing_expected_rev",400,"Proposal submitted without `expectedRev` / `rev`","Read the flow and pass its `rev`"),R("version_mismatch",409,"Unknown or unavailable `rev` / `expectedRev` snapshot","Re-read flows and resubmit"),R("proposal_not_found",404,"Unknown proposal id","List proposals and use an existing id"),R("proposal_not_pending",409,"Action needs an open proposal","Only pending or conflict proposals can withdraw / decide"),R("proposal_not_owned",403,"Withdraw of another token\u2019s proposal","Only the creating token can withdraw"),R("validation_failed",400,"Several validation errors at once (message `N validation errors`)","Fix every entry in errors[] and resubmit."),R("duplicate_node_id",400,"The same node id appears more than once in a full tab or batch","Give every node a unique id"),R("missing_node_id",400,"Node / op without an id","Include a non-empty string id"),R("missing_node_type",400,"Node without a `type`","Set `type` to an installed palette type name"),R("invalid_node_type",400,"`type` is not a string","Use the palette type name exactly"),R("invalid_nodes",400,"`flow.nodes` / `flow.configs` not an array","Send arrays (use `[]` to clear)"),R("invalid_node",400,"Node entry is not an object","Send `{ id, type, \u2026 }` objects"),R("invalid_wires",400,"Bad wires shape, missing target, or more ports than outputs","Wire only to existing node ids / ports"),R("z_mismatch",400,"Node `z` differs from the flow id","Set `z` to the tab id or omit it"),R("node_already_exists",400,"`add` op for an id already in the flow","Use `set` or a new id"),R("protected_property",400,"Tried to change id / type / z","Omit id, type, and z from edits"),R("node_not_found",404,"Unknown node (single error only; several errors \u2192 `validation_failed`)","Re-read the flow"),R("field_not_found",404,"Unknown field on a node","Re-read the node"),R("text_not_found",400,"`replace_text` find had no match","Re-read field; find is exact"),R("ambiguous_match",409,"`replace_text` matched more than once","Pass `occurrence` (1-based) or unique find"),R("unknown_node_type",400,"Proposal uses a type that is not installed","List node-types / palette search; ask user to install"),R("node_type_not_found",404,"Unknown type on node-types detail","List types or search palette"),R("chunk_conflict",409,"Same draft seq re-sent with different data","Re-send identical data or start a new draft",Pn),R("missing_chunks",400,"Draft submit with gaps in seq","Upload every seq from 0 through max",Pn),R("draft_hash_mismatch",400,"Draft sha256 does not match assembled bytes","Send the hex sha256 of the assembled UTF-8",Pn),R("missing_config_node",201,"Warning: node has no (or an unresolved) config node yet","Configure before deploy if needed; see `candidates`",re,"warning"),R("unwired_node",201,"Warning: node has outputs but no wires","Wire it, or ignore while scaffolding",re,"warning"),R("incomplete_wires",201,"Warning: fewer wire ports than outputs","Add ports if outputs should connect",re,"warning"),R("redacted_placeholder_mismatch",400,"A `[REDACTED]` placeholder was added, duplicated, moved, or its env/header name changed","Keep placeholders exactly where they were shown (same line, same name) or supply a real value",re),R("secret_destination_changed",201,"Warning: a node holding a restored secret changed where it sends data (url/host/method/headers/\u2026)","Reviewer must verify the new destination before approving",re,"warning"),R("secret_node_code_changed",201,"Warning: a node holding a restored secret had its code changed","Reviewer must check the code does not send the secret elsewhere",re,"warning"),R("secret_wires_changed",201,"Warning: a node holding a restored secret had its output wires changed","Reviewer must check where its messages now go",re,"warning"),R("secret_scope_changed",201,"Warning: tab/group env or a config node scoping a secret-holding node changed","Reviewer must check env-resolved hosts/URLs (e.g. `${API_HOST}`)",re,"warning"),R("outbound_node_changed",201,"Warning: proposal adds/changes an outbound-capable node (http request/response, mqtt, tcp, udp, email, websocket, link out/call, function, exec, file, or any node with url/host fields)","Reviewer must check it cannot send secret data elsewhere",re,"warning"),R("secret_tab_changed",201,"Warning: proposal with restored secrets also changes other nodes in the tab","Review every change before approving",re,"warning"),R("secret_reference_added",201,"Warning: an added/changed node now reads a secret-named env/global/flow value (env.get, $env, ${\u2026}, typed input)","Reviewer must check the value is not sent anywhere unexpected",re,"warning"),R("conflict",200,"Status: open proposal has conflicting editor changes; approval blocked","Read the current flow and resolve the proposal",re,"status"),R("expired",200,"Status: open proposal expired after the configured TTL (default 7 days)","Re-read the flow and submit a new proposal",re,"status"),R("superseded",200,"Legacy closed status; new approvals rebase other open proposals","Re-read the flow and submit a new proposal if still needed",re,"status"),R("upstream_error",502,"Node-RED runtime/flows error","Retry; check Node-RED logs"),R("internal_error",500,"Unexpected server error","Retry; check Node-RED logs")],Jf=new Map(Po.map(e=>[e.code,e]));a(Tr,"registryHint");a(Nd,"cell");a(Fd,"renderGetLinkErrorTable")});function Od(e){let t=pe(e),n=po(e)||void 0;return{proposalId:e.id,title:t.title,displayName:t.displayName,flowId:e.payload.flowId,...n?{flowLabel:n}:{}}}function fi(e,t){return e!==void 0&&Number.isFinite(e)&&e>0?e:t}var Yt,Xt,Pt,Zt,Eo,Vf,$d,Co,Io=q(()=>{"use strict";Te();Je();Fe();yr();et();Pe();a(Od,"auditProposalMeta");Yt=class extends Error{static{a(this,"ProposalRebaseAckRequiredError")}code="rebase_ack_required";statusCode=409;mergedHash;rebaseState;constructor(t,n,r){super(`Proposal '${t}' was rebased onto newer editor changes since you viewed it. Review the updated diff, then approve again with the new mergedHash.`),this.name="ProposalRebaseAckRequiredError",this.mergedHash=n,this.rebaseState=r}},Xt=class extends Error{static{a(this,"ProposalRebaseRaceError")}code="rebase_race";statusCode=409;constructor(t){super(`Proposal '${t}' could not be applied because the flows kept changing during approve. It is still pending \u2014 try again.`),this.name="ProposalRebaseRaceError"}},Pt=class extends Error{static{a(this,"ProposalDeployTimeoutError")}code="deploy_timeout";statusCode=504;constructor(){super("Deployment timed out; the deploy outcome is unknown. The proposal remains pending with deployPending set. Approvals wait until the runtime settles or the deploy hard cap is reached. Check the live flow before approving again."),this.name="ProposalDeployTimeoutError"}},Zt=class extends Error{static{a(this,"ProposalApproveQueueTimeoutError")}code="approve_queue_timeout";statusCode=504;constructor(){super("Approval timed out waiting in the queue. No deployment was started for this request. Check the live flow and retry."),this.name="ProposalApproveQueueTimeoutError"}},Eo=class extends Error{static{a(this,"DeployHardCapError")}},Vf=3;a(fi,"deadlineMs");$d=Promise.resolve(),Co=class{constructor(t,n,r,o,s={}){this.proposals=t;this.audit=n;this.adapter=r;this.getDeploymentType=o;this.serviceOptions=s}static{a(this,"ProposalService")}async approve(t,n,r={}){let o=this.proposals.get(t),s=o.deployPending!==void 0;if(!U(o.status))throw new Y(t,o.status);if(!s&&this.proposals.isApproving(t))throw new Re(t);let i=$d,d,l=new Promise(c=>{d=c});return $d=i.then(()=>l),new Promise((c,u)=>{let p=!1,f,h=new Promise((g,y)=>{f=setTimeout(()=>y(new Zt),fi(this.serviceOptions.approveQueueTimeoutMs,6e4)),f.unref?.()});a(async()=>{try{await Promise.race([i,h]),clearTimeout(f);let g=this.proposals.get(t);if(s&&g.status==="approved")return g;let y=this.proposals.beginApprove(t);return p=!0,await this.approveLocked(y,n,r,u)}finally{clearTimeout(f),p&&this.proposals.releaseLock(t),d()}},"run")().then(c,u)})}async approveLocked(t,n,r,o){let s=t.id,i,d=Od(t),l=r.deploymentType??this.getDeploymentType(),c=t.type;if(c!=="replace_flow"){let f=this.proposals.markFailed(s,n,`Unsupported proposal type '${c}'`);return this.audit.append("proposal.failed",n,{...d,reason:f.reason}),f}if(ko(t))throw this.proposals.releaseLock(s),ne(this.proposals,{flowId:t.payload.flowId}),new Y(s,this.proposals.get(s).status);let u=Oe(this.adapter),p=a((f,h)=>{let m=this.proposals.markApproved(s,n,h);return this.audit.append("proposal.approved",n,{...d,resultRev:h,liveRev:h,baseRev:f.expectedRev,alreadyApplied:!0}),m},"markAlreadyApplied");for(let f=0;f<Vf;f++){let h=await u.getFlows(),m=this.proposals.get(s),g=Wt(h.flows,m.payload.flowId,m.payload.flow),y=g&&gr(g);if(y&&(y===m.deployPending?.mergedHash||y===gr(m.payload.flow))){i=p(m,h.rev);break}let{result:A}=Ze(this.proposals,m,h,{duringApprove:!0});if(A.state==="conflict"||!A.merged||!A.mergedHash)throw this.audit.append("proposal.conflict",n,{...d,liveRev:h.rev,conflictCount:A.conflicts.length}),new Ke(s,si(A).conflicts);if(y===A.mergedHash){i=p(m,h.rev);break}if((A.state==="merged"||r.mergedHash!==void 0)&&r.mergedHash!==A.mergedHash)throw new Yt(s,A.mergedHash,A.state);let $=A.merged,L=A.mergedHash,D=!1;try{let H=await this.withDeployDeadline(()=>u.updateFlow({flowId:m.payload.flowId,flow:$,expectedRev:h.rev,deploymentType:l}),()=>{D=!0;let T=new Pt;this.proposals.recordDeployPending(s,{mergedHash:L,reason:"Deploy outcome unknown: the runtime has not settled. Approvals are waiting."}),this.audit.append("proposal.deploy_timeout",n,{...d,code:T.code}),o(T)},()=>{this.proposals.recordDeployPending(s,{mergedHash:L,queueReleased:!0,reason:"Deploy outcome unknown: the runtime did not settle before the hard cap. The approval queue was released. Check the live flow before approving again."}),o(new Pt)});i=this.proposals.markApproved(s,n,H.rev),this.audit.append("proposal.approved",n,{...d,resultRev:H.rev,flowId:m.payload.flowId,baseRev:m.expectedRev,liveRev:h.rev,rebaseState:A.state});break}catch(H){if(i||H instanceof Eo)throw H;if(D)throw this.proposals.recordDeployPending(s,void 0),H;if(H instanceof te)continue;let T=H instanceof Error?H.message:String(H),k=this.proposals.markFailed(s,n,T);return this.audit.append("proposal.failed",n,{...d,reason:T}),k}}if(!i)throw new Xt(s);try{await wr(this.proposals,this.adapter,{flowId:i.payload.flowId})}catch{}return i}async withDeployDeadline(t,n,r){let o=setTimeout(n,fi(this.serviceOptions.deployTimeoutMs,6e4));o.unref?.();let s,i=new Promise((d,l)=>{s=setTimeout(()=>{try{r()}finally{l(new Eo)}},fi(this.serviceOptions.deployHardCapMs,3e5)),s.unref?.()});try{return await Promise.race([t(),i])}finally{clearTimeout(o),clearTimeout(s)}}reject(t,n,r){let o=this.proposals.get(t);if(!U(o.status))throw new Y(t,o.status);let s=Od(o),i=this.proposals.markRejected(t,n,r.trim()||"Rejected by admin");return this.audit.append("proposal.rejected",n,{...s,reason:i.reason}),i}}});function we(e,t,n={}){return hi(e,t,n)}function Yf(e,t){return t==="excess_ports"?e==="mcp"?"This node has more wire ports than its type declares \u2014 remove extra ports or raise the node outputs, then retry.":e==="rest"?"This node has more wire ports than its type declares \u2014 trim wires[] or raise outputs, then retry.":"This node has more wire ports than its type declares \u2014 trim wires or raise outputs, then retry.":t==="missing_target"?e==="mcp"?"Wire targets must be node ids in this flow \u2014 add the node in the same patch or use get_flow ids.":e==="rest"?"Wire targets must be node ids in this flow \u2014 add the node in the same patch or use GET /flows/{id} ids.":"wires must list existing node ids (or add those nodes in the same patch).":e==="mcp"||e==="rest"?"Fix wires: targets must exist in this flow, and port count must not exceed the node outputs.":"wires must list existing node ids (or add those nodes in the same patch)."}function Xf(e){return e==="mcp"?"Call list_node_types or search_palette. Ask the user to install via Manage palette, then recheck. This API never installs.":e==="rest"?"Call GET /node-types?q= or GET /palette/search?q=. Ask the user to install via Manage palette, then recheck. This API never installs.":"Call GET \u2026/node-types?q= or \u2026/palette/search?q=. Ask the user to install via Manage palette, then recheck. This API never installs."}function _t(e,t,n,r){return e==="mcp"?t:e==="rest"?n:r}function hi(e,t,n={}){switch(e){case"version_mismatch":return _t(t,"Re-read with list_flows/get_flow and retry with the current rev.","Re-read the flow and retry with the current rev.","Re-read the flow and retry with the current rev.");case"node_not_found":return _t(t,"Call get_flow or get_node and use a node id from that response.","Call GET /flows/{id} and use a node id from that response.","Call GET \u2026/flows/<id> and use a node id from that response.");case"flow_not_found":return _t(t,"Call list_flows to list valid ids.","Call GET /flows to list valid ids.","Use an id from GET ./flows.");case"proposal_not_found":return _t(t,"Call list_proposals and use an id from that response.","List proposals and use an id from that response.","List proposals and use an id from that response.");case"proposal_not_owned":return"Only the access link that created an open proposal can withdraw it.";case"proposal_not_pending":return"Only open (pending or conflict) proposals can be withdrawn or rejected.";case"confirm_required":return"Add confirm=1 to the withdraw URL (required so link prefetchers cannot withdraw).";case"invalid_wires":return Yf(t,n.cause);case"node_type_not_found":case"unknown_node_type":return Xf(t);case"missing_node_type":return'Each add op needs node.type as a non-empty string (e.g. "inject").';case"invalid_line_range":return _t(t,'Pass lines as "A" or "A-B" (1-based inclusive). Reversed ranges (end < start) are invalid.',"Use lines=A or lines=A-B (1-based inclusive). Reversed ranges (end < start) are invalid.","Use lines=A or lines=A-B (1-based inclusive). Reversed ranges (end < start) are invalid.");case"line_range_out_of_bounds":return"start is past the end of the field \u2014 check totalLines and request a lower lines range.";case"ambiguous_match":return"find matched more than once \u2014 pass occurrence=N (1-based) or a more unique find string.";case"missing_expected_rev":return _t(t,"Pass expectedRev from list_flows/get_flow (top-level, next to type and payload).","Pass expectedRev from GET /flows or GET /flows/{id} (top-level, next to type and payload).","Pass rev from GET \u2026/flows or \u2026/flows/<id>.");case"missing_query":return _t(t,"Pass a non-empty q string.","q query parameter is required.","q query parameter is required.");case"unsupported_media_type":return"Send Content-Type: application/json with a JSON body.";case"text_not_found":return"Re-read the node field; find must match exactly (case-sensitive).";case"field_not_found":return"Re-read the node; field must be an existing string property (template, func, \u2026).";case"field_not_string":return _t(t,"This tool only returns string fields (template, func, \u2026). Use get_node for other property types.","This endpoint only returns string fields (template, func, \u2026). Use GET /nodes/{id} for other property types.","This endpoint only returns string fields (template, func, \u2026). Use GET /nodes/{id} for other property types.");default:return}}function Sr(e,t,n={}){let r=n.canonicalizeMessage!==!1,o=null;if(e instanceof Ke?o={statusCode:409,body:{error:e.message,code:e.code,conflicts:e.conflicts}}:e instanceof Yt?o={statusCode:409,body:{error:e.message,code:e.code,mergedHash:e.mergedHash}}:e instanceof Xt||e instanceof Pt||e instanceof Zt||e instanceof Re?o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}:e instanceof b?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,...e.details??{}}}:e instanceof te?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,expectedRev:e.expectedRev,currentRev:e.currentRev}}:e instanceof X?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,flowId:e.flowId}}:e instanceof me||e instanceof jt||e instanceof Y||e instanceof le?o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}:(e instanceof ze||e instanceof Le)&&(o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}),!o)return null;let s=o.body.code;if(r&&!Array.isArray(o.body.errors)){let i=nt[s];i&&(o.body.error=i)}if(typeof o.body.hint!="string"||!o.body.hint){let i=hi(s,t,{cause:o.body.cause})??Tr(s);i&&(o.body.hint=i)}return o}function Md(e,t,n={}){let o={error:n.error??nt[t]??"Request failed",code:t,...n.details??{}},s=hi(t,e,{cause:o.cause})??Tr(t);return s&&(o.hint=s),{statusCode:n.statusCode??400,body:o}}var nt,tt=q(()=>{"use strict";Te();Je();se();ie();_o();Io();nt={proposal_conflict:"Proposal conflicts with current editor changes",rebase_ack_required:"Review and acknowledge the merged proposal",rebase_race:"Flows kept changing during approval",deploy_timeout:"Deployment timed out; the deploy outcome is unknown. The proposal remains pending with deployPending set. Approvals wait until the runtime settles or the deploy hard cap is reached. Check the live flow before approving again.",approve_queue_timeout:"Approval timed out waiting in the queue. No deployment was started for this request. Check the live flow and retry.",conflict_not_found:"Conflict no longer exists",invalid_choice:"Invalid conflict choice",flow_id_mismatch:"Proposal flow id cannot change",node_not_found:"Node was not found",flow_not_found:"Flow was not found",field_not_found:"Field was not found",field_not_string:"Field is not a string",missing_query:"q query parameter is required",missing_flow_id:"Missing flow id",missing_node_id:"Missing node id",missing_field:"Missing field name",invalid_line_range:"lines must be A or A-B (1-based inclusive; end >= start)",line_range_out_of_bounds:"lines range is out of bounds",proposal_not_found:"Proposal was not found",proposal_not_owned:"Proposal was not created by this access link",proposal_not_pending:"Proposal is not pending",confirm_required:"Withdraw over GET requires confirm=1",unsupported_media_type:"Content-Type must be application/json",upstream_error:"Upstream request failed",internal_error:"Internal server error"};a(we,"channelErrorHint");a(Yf,"invalidWiresHint");a(Xf,"nodeTypeHint");a(_t,"pickChannel");a(hi,"hintFor");a(Sr,"mapErrorForChannel");a(Md,"buildChannelError")});var My={};Ki(My,{ACCESS_LINK_INVALID_MESSAGE:()=>Ot,ACCESS_LINK_MISSING_MESSAGE:()=>xe,AccessLinkStore:()=>an,EDITOR_SIDEBAR_ASSET_VERSION:()=>Cn,MAX_PROPOSAL_JSON_BYTES:()=>K,SettingsStore:()=>fn,acceptedMediaTypes:()=>Wc,areAiFeaturesEnabled:()=>Ce,bindSettingsStore:()=>Wr,createPluginServices:()=>us,default:()=>$y,getStorageDir:()=>ns,isAdminAuthEnabled:()=>oe,isMcpStreamRequest:()=>Gc,packageVersion:()=>En,registerRoutes:()=>Kc,renderEditorSidebarJs:()=>Lo,renderEditorSidebarLoaderHtml:()=>Ud,requireAccessLink:()=>Dt,resolveAiAccessSettings:()=>Ee});module.exports=eu(My);var zc=require("fs");var $r=require("fs"),Gn=require("path");se();function Mr(e){let t=(0,Gn.join)(e,".ai-access");return{root:t,proposals:(0,Gn.join)(t,"proposals"),logs:(0,Gn.join)(t,"logs"),settings:(0,Gn.join)(t,"config.json")}}a(Mr,"resolveAiAccessPaths");function ms(e){(0,$r.mkdirSync)(e,{recursive:!0,mode:hs});try{(0,$r.chmodSync)(e,hs)}catch{}}a(ms,"mkdirSecure");function Zi(e){ms(e.root),ms(e.proposals),ms(e.logs)}a(Zi,"ensureAiAccessDirs");$t();Be();function Vr(e){let t=Ae(e);return`auth-tokens${(t==="/"?"":t).replace(/\//g,"-")}`}a(Vr,"editorAuthTokenKey");Be();tt();function Zf(e){if(!e)return!1;let t=e.split(";")[0]?.trim().toLowerCase()??"";return t==="application/json"||t.endsWith("+json")}a(Zf,"isJsonContentType");function Qf(e){let t=e.headers["content-length"];if(typeof t=="string"&&t.length>0){let n=Number(t);if(Number.isFinite(n)&&n>0)return!0;if(n===0)return!1}return e.headers["transfer-encoding"]!==void 0}a(Qf,"hasRequestBody");function De(e="rest"){return(t,n,r)=>{let o=t.headers["content-type"];if(Zf(o)){r();return}if(!o&&!Qf(t)){r();return}let s=Md(e,"unsupported_media_type",{statusCode:415});n.status(s.statusCode).json(s.body)}}a(De,"requireJsonContentType");var eh="text/plain; charset=utf-8",th="text/html; charset=utf-8",nh="default-src 'none'; base-uri 'self'; form-action 'none'; frame-ancestors 'none'";function rh(e){let t=e.query?.format;return typeof t=="string"?t.toLowerCase():void 0}a(rh,"formatParam");function oh(e){let t=rh(e);if(t==="md"||t==="json")return"native";if(!e.get("accept"))return"html";let n=e.accepts(["text/html","application/json","text/markdown","text/plain"]);return n==="application/json"||n==="text/markdown"?"native":n==="text/plain"?"plain":"html"}a(oh,"chooseGetLinkRepresentation");function Dd(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}a(Dd,"escapeHtml");var sh=/(?<![A-Za-z0-9_.~\-/])\.\/[A-Za-z0-9._~\-/]+(?:\?[A-Za-z0-9._~\-=&%]+)?/g;function ih(e){return e.replace(sh,(t,n)=>{if(e.startsWith("&lt;",n+t.length))return t;let r=t.replace(/[.]+$/,""),o=t.slice(r.length);return r.length<=2||r.includes("..")||r.includes("//")||r.includes("&")?t:`<a href="${r}">${r}</a>${o}`})}a(ih,"linkifyEscaped");function ah(e,t){return`<!doctype html>
11
- <html><head><meta charset="utf-8">${t?`<base href="${Dd(t)}">`:""}<title>Node-RED AI Access</title></head><body><pre>${ih(Dd(e))}</pre></body></html>
12
- `}a(ah,"renderGetLinkHtml");function dh(e){let t=e.split("?")[0]??"",n=/^(.*?\/mcp\/a\/[A-Za-z0-9_-]+)(?:\/|$)/.exec(t);if(!(!n?.[1]||!/^[A-Za-z0-9._~\-/]*$/.test(n[1])))return`${n[1]}/`}a(dh,"getLinkBaseHref");function lh(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}a(lh,"prettyJson");function Hd(){return(e,t,n)=>{let r=oh(e),o=dh(e.originalUrl);t.vary("Accept"),t.setHeader("Cache-Control","no-store"),t.setHeader("Referrer-Policy","no-referrer"),t.setHeader("X-Robots-Tag","noindex, nofollow"),t.setHeader("X-Content-Type-Options","nosniff");let s=t.send.bind(t);t.send=(i=>{let d=String(t.getHeader("Content-Type")??"").toLowerCase(),l=d.startsWith("application/json"),c=d.startsWith("text/markdown");if(r==="native"||!l&&!c||t.headersSent)return s(i);let u=typeof i=="string"?i:Buffer.isBuffer(i)?i.toString("utf8"):void 0;return u===void 0?s(i):r==="plain"?(t.setHeader("Content-Type",eh),s(u)):(t.setHeader("Content-Type",th),t.setHeader("Content-Security-Policy",nh),s(ah(l?lh(u):u,o)))}),n()}}a(Hd,"getLinkContentNegotiation");Dr();Be();function ch(e){let t=Se(e.settings.httpAdminRoot),n=ht(e.settings.httpAdminRoot),r="0.2.0-beta.7",o=[{method:"GET",path:"/",auth:"none",summary:"This discovery index"},{method:"GET",path:"/openapi.yaml",auth:"none",summary:"OpenAPI 3.1 specification (YAML)"},{method:"GET",path:"/openapi.json",auth:"none",summary:"OpenAPI 3.1 specification (JSON)"},{method:"GET",path:"/status",auth:"admin-or-access-link",summary:"Plugin status and caller info"},{method:"GET",path:"/flows",auth:"admin-or-access-link",summary:"List flows + rev"},{method:"GET",path:"/flows/{id}",auth:"admin-or-access-link",summary:"Get one flow tab + rev (?summary=1 for node list without field contents)"},{method:"GET",path:"/nodes/{id}",auth:"admin-or-access-link",summary:"Get one full node (optional ?flow=)"},{method:"GET",path:"/nodes/{id}/field/{name}",auth:"admin-or-access-link",summary:"Get one node field (?lines=A-B optional)"},{method:"GET",path:"/search",auth:"admin-or-access-link",summary:"Text search across string fields (?q=&flow=)"},{method:"GET",path:"/node-types",auth:"admin-or-access-link",summary:"List installed palette node types (?q= filter)"},{method:"GET",path:"/node-types/{type}",auth:"admin-or-access-link",summary:"Detail for one installed node type (defaults JSON, inputs/outputs, help; ?help=0 omits help)"},{method:"GET",path:"/palette/search",auth:"admin-or-access-link",summary:"Search Node-RED catalogue (recommend only; never installs) (?q=)"},{method:"POST",path:"/proposals",auth:"access-link",summary:"Create a replace_flow proposal"},{method:"GET",path:"/proposals",auth:"admin-or-access-link",summary:"List proposals (admin: all; link: own)"},{method:"GET",path:"/proposals/{id}",auth:"admin-or-access-link",summary:"Get one proposal"},{method:"GET",path:"/proposals/{id}/diff",auth:"admin-or-access-link",summary:"Diff proposal vs current flow"}];return{ok:!0,name:"node-red-ai-access",version:r,description:"Temporary, owner-approved Node-RED access for AI clients over plain HTTP (REST) or MCP.",auth:{scheme:"Bearer",header:"Authorization: Bearer <token>",description:"An admin with * mints tokens on the /ai-access dashboard (Access tokens tab) (POST /links only \u2014 never under GET /mcp/a/<token>). Each token has kind get_link | rest | mcp and only works on that surface. Send rest/mcp tokens as Authorization: Bearer <token>. Missing/invalid \u2192 401 access_link_missing / access_link_invalid; wrong kind \u2192 403 wrong_token_kind. GET /mcp/a links cannot create tokens.",tokenKinds:["get_link","rest","mcp"],mint:{path:"/links",method:"POST",auth:"admin",note:"Admin * only. Not available under /mcp/a/<token>."}},openapi:`${t}/openapi.yaml`,openapiJson:`${t}/openapi.json`,mcp:n,apiBase:t,getLink:{base:"/mcp/a/<token>",canMintTokens:!1,note:"Self-contained GET surface for get_link tokens only. No POST /links equivalent."},endpoints:o}}a(ch,"buildDiscoveryIndex");function mi(e){return(t,n)=>{n.status(200).json(ch(e))}}a(mi,"discoveryIndexHandler");function jd(){return(e,t)=>{t.status(404).json({error:"Not found",code:"not_found"})}}a(jd,"mcpApiNotFoundHandler");var _n;function En(){return _n||(_n="0.2.0-beta.7",_n)}a(En,"packageVersion");var Cn=En();function uh(){return`#red-ui-sidebar-ai-access {
9
+ `);n.push({before:i>=0?o.slice(i+1):o,after:d>=0?s.slice(0,d):s})}return n}function vo(e){return JSON.stringify([e.before,e.after])}function wo(e){let t=new Map;return hr(e.text).forEach((n,r)=>{let o=vo(n),s=t.get(o)??[];s.push(r),t.set(o,s)}),t}function nd(e,t,n,r){if(e===t)return{text:n,restored:r.filter(f=>f.kind==="secret").length};let o=hr(e),s=hr(t);if(o.length>s.length)return{error:`contains ${o.length} [REDACTED] placeholders but the original had ${s.length}`};let i=wo({text:t,occurrences:r}),d=new Set([...i].filter(([,f])=>new Set(f.map(m=>r[m]?.kind)).size>1).map(([f])=>f)),l=new Map,c=e.split(J),u=c[0]??"",p=0;for(let f=0;f<o.length;f++){let m=o[f];if(!m)break;let h=vo(m),g=i.get(h)??[];if(d.has(h))return{error:"ambiguous literal and secret [REDACTED] occurrences"};let y=l.get(h)??0,w=g[y]??-1;if(w<0)return{error:"a [REDACTED] placeholder is not on an unchanged original line"};l.set(h,y+1);let v=r[w];v?.kind==="secret"&&p++,u+=(v?.value??J)+(c[f+1]??"")}return{text:u,restored:p}}function Bt(e){if(ee(e))for(let t of lf){let n=e[t];if(typeof n=="string"&&n.length>0)return`${t}=${n}`}}function yr(e,t){let n=a(s=>[s,...s.nodes??[],...s.configs??[]],"entities"),r=new Map(n(e).map(s=>[s.id,s])),o=[];for(let s of n(t)){let i=r.get(s.id);if(JSON.stringify(i?.env)===JSON.stringify(s.env))continue;let d=a(l=>{let c=new Set;return Array.isArray(l)?l.some(u=>!ee(u)||typeof u.name!="string"||!u.name?!1:c.has(u.name)?!0:(c.add(u.name),!1)):!1},"hasDuplicates");!d(i?.env)&&!d(s.env)||o.push({code:"duplicate_env_keys",...s.id?{nodeId:s.id}:{},property:"env",error:"Duplicate env names make pairing by name ambiguous when entries are reordered. Review the env values before approval."})}return o}function ri(e,t,n,r=[]){let o=a(c=>{let u=new Map;return Qs(c,{matcher:gr(kt()),count:0,fields:u}),u},"fieldsFor"),s=o(e),i=o(t),d=o(n),l=a((c,u,p,f,m=f,h=f)=>{if(typeof u=="string"){let g=s.get(`${e.id}\0${f}`);if(!g?.occurrences.some(E=>E.kind==="secret"))return u;let y=i.get(`${t.id}\0${m}`),w=d.get(`${n.id}\0${h}`),v=hr(g.text),L=wo(g),P=y?wo(y):new Map,I=w?wo(w):new Map,D=y?.occurrences.map(E=>E.value)??[],T=y?hr(y.text):[],x=y?.occurrences.some((E,A)=>{let S=T[A];return E.kind==="secret"&&S&&!L.has(vo(S))}),C=!1;return g.occurrences.forEach((E,A)=>{if(E.kind!=="secret")return;let S=v[A],N=S?vo(S):"",b=L.get(N)??[],O=P.get(N)??[],M=I.get(N)??[];if(b.length>1&&O.length>0&&O.length<b.length&&JSON.stringify(b.map(Kn=>g.occurrences[Kn]?.value))!==JSON.stringify(M.map(Kn=>w?.occurrences[Kn]?.value)))throw new mr;let F=a((Kn,eu)=>{let Zi=Kn.get(N)??[],[gs]=Zi;return Zi.length===1&&gs!==void 0&&eu?.occurrences[gs]?.kind==="secret"?gs:void 0},"matches"),le=F(L,g),cn=F(P,y),Ft=F(I,w);if(!P.has(N)&&!x)return;if(le===void 0||cn===void 0||Ft===void 0){C=!0;return}let Xi=w?.occurrences[Ft];y?.occurrences[cn]?.value===E.value&&Xi&&(D[cn]=Xi.value)}),C&&r.push({code:"secret_may_revert",nodeId:e.id,property:m,error:"Secret spans could not be aligned reliably. The proposed value was kept and may revert an editor secret change. Review this field before approval."}),!y||C?u:y.text.split(J).reduce((E,A,S)=>E+(S?D[S-1]??J:"")+A,"")}if(Array.isArray(u)){let g=Array.isArray(c)?c:[],y=Array.isArray(p)?p:[],w=new Set,v=new Set;return u.map((L,P)=>{let I=Bt(L),D=a((C,E)=>{let A=I===void 0?P:C.findIndex((S,N)=>!E.has(N)&&Bt(S)===I);return E.add(A),A},"match"),T=D(g,w),x=D(y,v);return l(g[T],L,y[x],`${f}[${T}]`,`${m}[${P}]`,`${h}[${x}]`)})}if(ee(u)){let g=ee(c)?c:{},y=ee(p)?p:{},w=a((v,L)=>v?`${v}.${L}`:L,"path");return Object.fromEntries(Object.entries(u).map(([v,L])=>[v,l(g[v],L,y[v],w(f,v),w(m,v),w(h,v))]).filter(([,v])=>v!==void 0))}return u},"walk");return l(e,t,n,"")}function rd(e,t){let n=Zs(t,[]);return"error"in nd(e,t,t,n)}function bo(e,t,n,r,o,s){if(typeof e=="string"){if(!e.includes(J))return e;let i=s.fields.get(`${r}\0${o}`);if(typeof t=="string"&&typeof n=="string"&&(i||n.includes(J))){let d=nd(e,t,n,i?.occurrences??Zs(n,[]));return"error"in d?(s.unresolved.push({nodeId:r,property:o,reason:d.error}),e):(d.restored>0&&(s.restored+=d.restored,s.restoredFields.push({nodeId:r,property:o})),d.text)}return s.unresolved.push({nodeId:r,property:o,reason:"no redacted secret exists at this node id and field"}),e}if(Array.isArray(e)){let i=Array.isArray(t)?t:[],d=Array.isArray(n)?n:[],l=new Set;return e.map(c=>{let u=ee(c)&&c.redacted===!0?Xs(c,"redacted"):c,p=Bt(u),f=-1;if(p!==void 0){let m=d.map((h,g)=>Bt(h)===p&&!l.has(g)?g:-1).filter(h=>h>=0);m.length>0&&(f=m[0]??-1)}return f>=0?(l.add(f),bo(u,i[f],d[f],r,`${o}[${f}]`,s)):bo(u,void 0,void 0,r,`${o}[?]`,s)})}if(ee(e)){let i=ee(t)?t:{},d=ee(n)?n:{},l={};for(let[c,u]of Object.entries(e))l[c]=bo(u,i[c],d[c],r,o?`${o}.${c}`:c,s);return l}return e}function Xs(e,t){let n={...e};return delete n[t],n}function cf(e){let t=Xs(e,pr);return Array.isArray(t.env)&&(t.env=t.env.map(n=>!ee(n)||n.redacted!==!0?n:Xs(n,"redacted"))),t}function od(e,t,n,r){let o=typeof e.id=="string"?e.id:"",s=o?t.get(o):void 0,i={},d=s?Qs({...s,id:o},r):void 0;for(let[l,c]of Object.entries(e))if(l!==pr){if(ed.has(l)&&Array.isArray(c)){i[l]=c.map(u=>ee(u)?od(u,t,n,r):u);continue}if(Xa.has(l)){i[l]=c;continue}i[l]=bo(c,d?.[l],s?.[l],o,l,n)}return cf(i)}function sd(e,t,n=kt()){let r=new Map;for(let l of t)ee(l)&&typeof l.id=="string"&&r.set(l.id,l);let o=new Map,s={matcher:gr(n),count:0,fields:o},i={fields:o,restored:0,restoredFields:[],unresolved:[]};return{flow:od(e,r,i,s),restored:i.restored,restoredFields:i.restoredFields,unresolved:i.unresolved}}function hf(e){return typeof e.type=="string"&&pf.has(e.type)?!0:ff.some(t=>typeof e[t]=="string"&&e[t]!=="")}function Ya(e){let t=new Set,n=a((r,o)=>{if(!(o>12)){if(typeof r=="string"){if(r.length>Qa)return;let s=/(?:\b(?:env|global|flow)\.get|\$env)\([ \t]{0,8}["'`]([^"'`\r\n]{1,128})["'`]|\$\{([A-Za-z_][\w.-]{0,127})\}/g,i;for(;(i=s.exec(r))!==null;)t.add(i[1]??i[2]??"");return}if(Array.isArray(r)){for(let s of r)n(s,o+1);return}if(ee(r))for(let[s,i]of Object.entries(r))[`${s}Type`,s==="v"?"vt":"",s==="to"?"tot":"",s==="value"?"type":"",s==="valueValue"?"valueType":"",s==="keyValue"?"keyType":""].some(c=>c&&typeof r[c]=="string"&&mf.has(r[c]))&&typeof i=="string"&&t.add(i),n(i,o+1)}},"walk");return n(e,0),t.delete(""),t}function Sn(e,t){return JSON.stringify(e)===JSON.stringify(t)}function ad(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]);for(let r of n)if(!(id.has(r)||r==="nodes"||r==="configs")&&!Sn(e[r],t[r]))return!0;return!1}function gf(e,t,n){let r=new Set([n]),o=typeof e.g=="string"?e.g:void 0;for(let s=0;o&&s<32&&!r.has(o);s++){r.add(o);let i=t.get(o);o=i&&typeof i.g=="string"?i.g:void 0}for(let s of Object.values(e))typeof s=="string"&&s!==e.id&&t.get(s)?.z===void 0&&t.has(s)&&r.add(s);for(let[s,i]of t)i.type==="global-config"&&r.add(s);return[...r]}function yf(e,t){return e.filter(n=>n.nodeId===t).map(n=>n.property).join(", ")}function wf(e,t,n,r){let o=[],s=uf.filter(d=>!Sn(n[d],r[d]));s.length>0&&o.push({code:"secret_destination_changed",error:`SECURITY: node ${e} holds a secret (${t}) restored from the live flow, and this proposal changes where it sends data (${s.join(", ")}). Verify the new destination before approving.`,nodeId:e,property:s.join(",")});let i=[...Za].filter(d=>!Sn(n[d],r[d]));return i.length>0&&o.push({code:"secret_node_code_changed",error:`Node ${e} holds a secret (${t}) and its code changed (${i.join(", ")}). Check the new code does not send the secret anywhere unexpected.`,nodeId:e,property:i.join(",")}),Sn(n.wires,r.wires)||o.push({code:"secret_wires_changed",error:`SECURITY: node ${e} holds a secret (${t}) and its output wires changed. Check where its messages (which may carry the secret) now go.`,nodeId:e,property:"wires"}),o}function bf(e,t,n,r,o,s){let i=[];for(let d of gf(t,n,o)){if(d===e||s.has(d))continue;let l=n.get(d),c=d===o,u=r.get(d)??(l&&!c&&l.z===void 0?l:void 0);if(!(c?!Sn(l?.env,u?.env):l!==void 0&&u!==void 0&&ad(l,u)))continue;s.add(d);let f=c?"tab":typeof l?.type=="string"?l.type:"node";i.push({code:"secret_scope_changed",error:`SECURITY: ${f} ${d} scopes or configures secret-holding node ${e} (env vars like \${HOST} or a config node) and it changed. Check URLs/hosts it resolves to.`,nodeId:d,property:c?"env":"config"})}return i}function vf(e,t,n,r,o){let s=[],i=[];for(let[d,l]of n){if(d===r)continue;let c=t.get(d);if(c&&!ad(c,l))continue;e.includes(d)||i.push(d);let u=typeof l.type=="string"?l.type:"node";hf(l)&&!e.includes(d)&&s.push({code:"outbound_node_changed",error:`SECURITY: this proposal ${c?"changes":"adds"} outbound-capable node ${d} (${u}). Check where it sends data before approving.`,nodeId:d,property:[...new Set([...Object.keys(c??{}),...Object.keys(l)])].filter(m=>!id.has(m)&&m!=="nodes"&&m!=="configs"&&!Sn(c?.[m],l[m])).sort().join(",")});let p=c?Ya(c):new Set,f=[...Ya(l)].filter(m=>!p.has(m)&&o.matches(m));f.length>0&&s.push({code:"secret_reference_added",error:`SECURITY: node ${d} (${u}) now reads secret-named value(s) ${f.slice(0,5).join(", ")} (env / global / flow). The AI cannot see these values but the flow will use them at runtime; check they are not sent anywhere unexpected.`,nodeId:d,property:f.slice(0,5).join(",")})}if(e.length===0)return s;for(let[d,l]of t)l.z===r&&!n.has(d)&&i.push(d);if(i.length>0){let d=i.slice(0,10).join(", ")+(i.length>10?", \u2026":"");s.push({code:"secret_tab_changed",error:`This proposal restores secrets in this tab and also changes ${i.length} other node(s) (${d}). Review every change: any of them could route secret data elsewhere.`,nodeId:r,property:"nodes"})}return s}function kf(e,t){let n=new Map;n.set(t,e);for(let r of["nodes","configs"]){let o=e[r];if(Array.isArray(o))for(let s of o)ee(s)&&typeof s.id=="string"&&n.set(s.id,s)}return n}function dd(e,t,n,r=kt()){let o=[...new Set(e.map(u=>u.nodeId))],s=String(t.id),i=new Map;for(let u of n)ee(u)&&typeof u.id=="string"&&i.set(u.id,u);let d=kf(t,s),l=[],c=new Set;for(let u of o){let p=i.get(u),f=d.get(u);!p||!f||(l.push(...wf(u,yf(e,u),p,f)),l.push(...bf(u,p,i,d,s,c)))}return l.push(...vf(o,i,d,s,gr(r))),l}var pr,Jp,Vp,Vs,Xa,Yp,Za,Qa,ed,Wa,Ga,yo,Ka,Qp,Ja,Ys,ni,Va,lf,mr,uf,pf,ff,mf,id,Pe=q(()=>{"use strict";pn();ke();pn();pr="_aiRedactions",Jp=new Set(["TYPE","TYPES","MODE","METHOD","FIELD","FIELDS","PROP","PROPERTY","FORMAT","TIMEOUT","EXPIRY","EXPIRES","TTL","LENGTH","LEN","SIZE","COUNT","ENABLED","DISABLED","ENABLE","USE","URL","URI","ENDPOINT","HOST","PORT","PATH","HEADER","NAME","LABEL","FILE"]),Vp=new Set(["basic","digest","bearer","none","true","false","null","undefined","oauth","oauth2","jwt","apikey","api_key","token","password","header","query","body","cookie","msg","flow","global","env","str","num","json","bool","other"]),Vs=new Set(["env","msg","flow","global","jsonata","bool","date","node","cred"]),Xa=new Set(["id","type","z","g","x","y","w","h","wires","inputs","outputs","links","scope","icon","color","style","d","l","in","out","rev"]),Yp=new Set(["name","label","info"]),Za=new Set(["func","initialize","finalize"]),Qa=256*1024,ed=new Set(["nodes","configs","subflows"]),Wa=["name","p","keyValue","key","property","k"],Ga={value:"type",v:"vt",to:"tot",valueValue:"valueType"},yo=new Map;a(gr,"createSecretNameMatcher");a(kt,"currentSecretNamePatterns");a(fr,"isSecretLikeValue");Ka=new WeakMap;a(Xp,"contentRules");a(Zp,"buildContentRules");Qp=["PASSWORD","PASSWD","PASSPHRASE","SECRET","CLIENT_SECRET","TOKEN","API_KEY","APIKEY","PRIVATE","CREDENTIAL","PWD","PW"];a(ef,"shannonEntropy");a(tf,"looksRandom");a(nf,"unquotedRule");a(rf,"decodeURIComponentSafe");a(of,"parseConnSegment");a(sf,"connectionStringSpans");Ja=new WeakMap;a(af,"scanTextForSecrets");a(Zs,"classifyPlaceholders");a(ee,"isPlainObject");a(df,"isFlowEntity");Ys=class{constructor(t,n){this.ctx=t;this.entityId=n}static{a(this,"EntityRedactor")}paths=[];record(t,n){this.paths.includes(t)||this.paths.push(t),this.ctx.count+=n.length||1,this.ctx.secrets?.set(`${this.entityId}\0${t}`,n)}string(t,n,r){if(r&&fr(t)||t.length>Qa)return this.record(n,[t]),this.ctx.fields?.set(`${this.entityId}\0${n}`,{text:J,occurrences:[{kind:"secret",value:t}]}),J;let o=n.split(/[.[]/,1)[0]??"",s=af(t,this.ctx.matcher,{code:Za.has(o)});return this.ctx.fields&&s.text.includes(J)&&this.ctx.fields.set(`${this.entityId}\0${n}`,{text:s.text,occurrences:Zs(t,s.spans)}),s.secrets.length>0?(this.record(n,s.secrets),s.text):t}env(t,n){return t.map((r,o)=>{if(!ee(r))return r;let s=typeof r.type=="string"?r.type:"";if(s==="cred"||typeof r.value!="string"||Vs.has(s))return r;let i=typeof r.name=="string"?r.name:"",d=r.value,l=this.string(d,`${n}[${o}].value`,this.ctx.matcher.matches(i));return l===d?r:{...r,value:l,redacted:!0}})}value(t,n,r,o){if(typeof t=="string"){if(Yp.has(r))return this.string(t,n,!1);let s=o?.[`${r}Type`],i=typeof s=="string"&&Vs.has(s);return this.string(t,n,!i&&this.ctx.matcher.matches(r))}return Array.isArray(t)?r==="env"?this.env(t,n):t.map((s,i)=>this.value(s,`${n}[${i}]`,"",void 0)):ee(t)?this.object(t,n):t}object(t,n){let r=!1;for(let s of Wa){let i=t[s];if(typeof i=="string"&&this.ctx.matcher.matches(i)){r=!0;break}}let o={};for(let[s,i]of Object.entries(t)){let d=n?`${n}.${s}`:s;if(r&&s in Ga&&typeof i=="string"){let l=Ga[s],c=l?t[l]:void 0,u=typeof c=="string"&&Vs.has(c);o[s]=this.string(i,d,!u);continue}if(Wa.includes(s)&&typeof i=="string"){o[s]=this.string(i,d,!1);continue}o[s]=this.value(i,d,s,t)}return o}};a(Qs,"redactEntity");a(ei,"redactValueInner");a(ct,"redactFlowData");a(ti,"redactConflictLabels");a(Ye,"redactProposalForAi");ni=Symbol.for("ai-access.rawFlowAdapter"),Va=new WeakMap;a(At,"aiRedactingAdapter");a(td,"isRedactingAdapter");a($e,"rawFlowAdapter");a(Xe,"adapterForRequest");a(ko,"countRedactionPlaceholders");a(hr,"placeholderContexts");a(vo,"contextKey");a(wo,"indexOccurrences");a(nd,"restoreString");lf=["name","keyValue","p","property","key","k"];a(Bt,"stableElementKey");a(yr,"duplicateEnvWarnings");mr=class extends Error{static{a(this,"SecretAlignmentConflictError")}constructor(){super("A duplicate secret line was removed while its live values changed. Resolve this field against the live flow."),this.name="SecretAlignmentConflictError"}};a(ri,"preserveLiveSecrets");a(rd,"hasNewLiteralPlaceholder");a(bo,"restoreWalk");a(Xs,"withoutKey");a(cf,"stripMarkers");a(od,"restoreEntity");a(sd,"restoreRedactedPlaceholders");uf=["type","url","host","hostname","server","broker","port","path","method","headers","endpoint","uri","protocol","proxy","tls","to","ret"],pf=new Set(["http request","http response","link out","link call","mqtt out","mqtt-broker","tcp out","tcp request","udp out","e-mail","websocket out","websocket-client","websocket-listener","function","exec","file"]),ff=["url","host","hostname","server","broker","endpoint","uri"];a(hf,"isOutboundNode");mf=new Set(["env","global","flow"]);a(Ya,"referencedNames");id=new Set(["x","y","w","h","l"]);a(Sn,"sameJson");a(ad,"nodeChanged");a(gf,"scopeIds");a(yf,"restoredFieldList");a(wf,"secretNodeWarnings");a(bf,"scopeWarnings");a(vf,"tabWarnings");a(kf,"proposedNodeMap");a(dd,"secretChangeWarnings")});function qt(e){if(e===null||typeof e!="object")return JSON.stringify(e)??"undefined";if(Array.isArray(e))return`[${e.map(qt).join(",")}]`;let t=e;return`{${Object.keys(t).filter(r=>t[r]!==void 0).sort().map(r=>`${JSON.stringify(r)}:${qt(t[r])}`).join(",")}}`}function Tf(e){return(0,ld.createHash)("sha256").update(qt(e)).digest("hex").slice(0,16)}function ye(e){return Array.isArray(e)?e.filter(t=>t&&typeof t=="object"&&typeof t.id=="string"):[]}function Rn(e){let t=new Map;for(let n of ye(e.nodes))t.set(n.id,{node:n,config:!1});for(let n of ye(e.configs))t.set(n.id,{node:n,config:!0});return t}function Ze(e){let t={};for(let n of ai)e[n]!==void 0&&(t[n]=e[n]);return t}function Ao(e,t){let n=new Set([...Object.keys(e??{}),...Object.keys(t??{})]),r=[];for(let o of n)ud.has(o)||qt(e?.[o])!==qt(t?.[o])&&r.push(o);return r.sort()}function oi(e){if(e===void 0)return new Map;if(!Array.isArray(e))return;let t=new Map;for(let n of e){if(!Tn(n)||typeof n.name!="string"||!n.name||t.has(n.name))return;t.set(n.name,n)}return t}function Tn(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function pd(e,t){let n=[];return wr(e,t,e,void 0,n),n.sort()}function wr(e,t,n,r,o){let s=[],i=a((f,m)=>qt(f)===qt(m),"same"),d=new Set([e.type,t.type,n.type].includes("function")?Af:[]);d.size&&(!i(e.outputs,t.outputs)||!i(e.outputs,n.outputs))&&d.add("wires");let l=a(f=>[...d].some(m=>!i(e[m],f[m])),"changedInGroup"),c=l(t)&&l(n),u=a((f,m,h,g)=>{if(i(f,m))return h;if(g==="env"){let y=oi(f),w=oi(m),v=oi(h);if(y&&w&&v){let L=[];for(let P of new Set([...w.keys(),...v.keys(),...y.keys()])){let I=u(y.get(P),w.get(P),v.get(P),`env[${JSON.stringify(P)}]`);I!==void 0&&L.push(I)}return L}}if((g==="credentials"||g.startsWith("credentials.")||g.startsWith("env["))&&Tn(m)&&(Tn(h)||h===void 0&&f===void 0)&&(f===void 0||Tn(f))){let y=Tn(f)?f:{};return p(y,m,Tn(h)?h:{},g)}return o?.push(g),i(f,h)||xf.has(g)?m:(s.push(g),r==="live"?h:m)},"merge"),p=a((f,m,h,g)=>{let y=[];for(let w of new Set([...Object.keys(f),...Object.keys(m),...Object.keys(h)])){if(!g&&ud.has(w))continue;let v=g?`${g}.${w}`:w,L;!g&&c&&d.has(w)?((!i(f[w],m[w])||!i(f[w],h[w]))&&s.push(w),i(f[w],m[w])||o?.push(w),L=r==="live"?h[w]:m[w]):L=u(f[w],m[w],h[w],v),L!==void 0&&y.push([w,L])}return Object.fromEntries(y)},"mergeObject");return{value:p(e,t,n,""),conflicts:s.sort()}}function xt(e){if(!e)return{};let t={};return typeof e.type=="string"&&(t.nodeType=e.type),typeof e.name=="string"&&e.name&&(t.nodeName=e.name.slice(0,120)),t}function fd(e,t){let n=e.find(r=>r.id===t&&(r.type==="tab"||r.type==="subflow"));if(n)return{tab:n,nodes:e.filter(r=>r.id!==t&&r.z===t)}}function zt(e,t,n){let r=fd(e,t);if(!r)return;let o=new Set(ye(n?.configs).map(c=>c.id)),s=new Set(ye(n?.nodes).map(c=>c.id)),i=[],d=[];for(let c of r.nodes)((o.has(c.id)?!0:s.has(c.id)?!1:c.type!=="group"&&c.x===void 0&&c.y===void 0&&c.wires===void 0)?d:i).push(c);let l={id:t,type:r.tab.type,...Ze(r.tab),nodes:i};return d.length>0&&(l.configs=d),l}function xo(e,t){if(typeof e=="string")return e.includes(J)&&(typeof t!="string"||rd(e,t));if(Array.isArray(e)){let n=Array.isArray(t)?t:[],r=new Set;return e.some((o,s)=>{let i=Bt(o),d=i===void 0?s:n.findIndex((l,c)=>!r.has(c)&&Bt(l)===i);return r.add(d),xo(o,n[d])})}if(e&&typeof e=="object"){let n=t&&typeof t=="object"?t:{};return Object.entries(e).some(([r,o])=>xo(o,n[r]))}return!1}function hd(e){let{flowId:t,proposed:n,liveFlows:r,liveRev:o}=e,s=[],i=a(b=>{s.length<Sf&&s.push(b)},"push"),d=fd(r,t);if(!d)return i({kind:"tab_deleted",nodeId:t,message:"The flow tab this proposal edits was deleted in the editor.",choices:[]}),{state:"conflict",rev:o,conflicts:s,liveChangedIds:[t]};let l=e.base??(e.baseRev===o?zt(r,t,n):void 0);if(l===void 0)return i({kind:"base_unavailable",nodeId:t,message:"This proposal was stored before base snapshots existed and the flows changed since, so live edits cannot be told apart from the proposal. Ask the AI to resubmit, or apply the proposal over the live tab.",choices:["proposed"]}),{state:"conflict",rev:o,conflicts:s,liveChangedIds:[]};let c=zt(r,t,n),u=Rn(l),p=Rn(c),f=Rn(n),m=[],h=Ze(l),g=Ze(d.tab),y=Ze(n),w=Ao(h,g),v=wr(h,y,g),L=v.value;w.length>0&&m.push(t),v.conflicts.length>0&&i({kind:"tab_changed_both",nodeId:t,fields:v.conflicts,message:"Both the editor and the proposal changed the same flow tab fields.",choices:["live","proposed"]}),L={...L};for(let b of ai)g[b]!==void 0&&L[b]===void 0&&(L[b]=b==="env"?[]:cd[b]);let P=new Map,I=new Set([...u.keys(),...p.keys(),...f.keys()]),D=new Map;for(let b of r)b.id!==t&&b.z!==t&&D.set(b.id,b);for(let b of I){let O=u.get(b),M=p.get(b),F=f.get(b),le=O&&M?Ao(O.node,M.node):[],cn=O&&F?Ao(O.node,F.node):[];if((O&&(!M||le.length>0)||!O&&M)&&m.push(b),F&&O&&D.has(b))i({kind:"id_collision",nodeId:b,...xt(F.node),message:"This node id now exists outside this flow tab.",choices:["live"]});else if(O&&M&&F){let Ft=wr(O.node,F.node,M.node);Ft.conflicts.length===0?P.set(b,{...F,node:Ft.value}):i({kind:"node_changed_both",nodeId:b,...xt(M.node),fields:Ft.conflicts,message:"Both the editor and the proposal changed the same node fields.",choices:["live","proposed"]})}else O&&M&&!F?le.length===0?P.set(b,null):i({kind:"node_deleted_in_proposal_modified_live",nodeId:b,...xt(M.node),fields:le,message:"The proposal deletes this node, but it was changed in the editor.",choices:["live","proposed"]}):O&&!M&&F?cn.length===0?P.set(b,null):i({kind:"node_deleted_live",nodeId:b,...xt(F.node),fields:cn,message:"This node was deleted in the editor, but the proposal changes it.",choices:["live","proposed"]}):O&&!M&&!F?P.set(b,null):!O&&M&&F?i({kind:"id_collision",nodeId:b,...xt(F.node),fields:Ao(M.node,F.node),message:"The proposal adds a node whose id was also added in the editor.",choices:["live","proposed"]}):!O&&M&&!F?P.set(b,M):!O&&!M&&F&&(D.get(b)??(b===t?d.tab:void 0)?i({kind:"id_collision",nodeId:b,...xt(F.node),message:"The proposal adds a node whose id now exists on another flow tab.",choices:["live"]}):P.set(b,F))}if(s.length>0)return{state:"conflict",rev:o,conflicts:s,liveChangedIds:si(m)};let T=[],x=[],C=new Set,E=a(b=>{if(C.has(b))return;let O=P.get(b);C.add(b),O&&(O.config?x:T).push(md(O.node))},"emit");for(let b of ye(n.nodes))E(b.id);for(let b of ye(n.configs))E(b.id);for(let b of ye(c.nodes))E(b.id);for(let b of ye(c.configs))E(b.id);let A={id:t,type:d.tab.type,...L,nodes:T};(x.length>0||Array.isArray(n.configs))&&(A.configs=x);let S=new Set([...T,...x].map(b=>b.id)),N=new Set(r.filter(b=>b.id!==t&&b.z!==t).map(b=>b.id));for(let b of[...T,...x]){let O=[];if(Array.isArray(b.wires)){for(let F of b.wires)if(Array.isArray(F))for(let le of F)typeof le=="string"&&!S.has(le)&&O.push(le)}if(typeof b.g=="string"&&b.g.length>0&&!S.has(b.g)&&O.push(b.g),b.type==="group"&&Array.isArray(b.nodes))for(let F of b.nodes)typeof F=="string"&&!S.has(F)&&O.push(F);if(Array.isArray(b.links))for(let F of b.links)typeof F=="string"&&!S.has(F)&&!N.has(F)&&O.push(F);let M=[...new Set(O)].sort();M.length>0&&i({kind:"dangling_reference",nodeId:b.id,...xt(b),fields:M,message:"After merging, this node points to a node that no longer exists (deleted in the editor).",choices:["live"]}),xo(b,p.get(b.id)?.node)&&i({kind:"redacted_placeholder",nodeId:b.id,...xt(b),message:"A [REDACTED] placeholder would be deployed over a real value. Ask the AI to resubmit.",choices:[]})}if(d.tab.type==="subflow"){let b=new Set;for(let O of[d.tab.in,d.tab.out])if(Array.isArray(O)){for(let M of O)if(Array.isArray(M?.wires))for(let F of M.wires)typeof F?.id=="string"&&!S.has(F.id)&&b.add(F.id)}b.size>0&&i({kind:"dangling_reference",nodeId:t,fields:[...b].sort(),message:"A subflow port points to a node that would no longer exist.",choices:[]})}return xo(L,g)&&i({kind:"redacted_placeholder",nodeId:t,message:"A [REDACTED] placeholder would be deployed into the tab settings. Ask the AI to resubmit.",choices:[]}),s.length>0?{state:"conflict",rev:o,conflicts:s,liveChangedIds:si(m)}:Rf(A,n,o,[],si(m))}function Rf(e,t,n,r,o){let s=br(e);return{state:s===br(t)?"clean":"merged",rev:n,conflicts:r,merged:e,mergedHash:s,liveChangedIds:o}}function br(e){return Tf(Pf(e))}function Pf(e){let t=[...ye(e.nodes),...ye(e.configs)].map(md).sort((n,r)=>n.id<r.id?-1:n.id>r.id?1:0);return{...cd,...Ze(e),nodes:t}}function md(e){if(!("z"in e))return e;let t={...e};return delete t.z,t}function si(e){return[...new Set(e)]}function gd(e){let{flowId:t,nodeId:n,choice:r,liveFlows:o}=e,s=[],i=zt(o,t,e.proposed);if(!i)throw new Error("flow tab deleted");if(e.base===void 0){if(r!=="proposed")throw new Error("invalid choice");return{base:St(i),proposed:St(e.proposed),warnings:s}}let d=St(e.base),l=St(e.proposed);if(r==="proposed"&&(s.push(...yr(d,l).filter(m=>m.nodeId===n)),s.push(...yr(i,l).filter(m=>m.nodeId===n&&!s.some(h=>h.nodeId===m.nodeId)))),n===t){let m=Ze(i),h=r==="proposed"?ri({id:t,type:i.type??"tab",...Ze(d)},{id:t,type:i.type??"tab",...Ze(l)},{id:t,type:i.type??"tab",...m},s):Ze(l),g=wr(Ze(d),h,m,r).value;for(let y of ai)delete d[y],m[y]!==void 0&&(d[y]=St(m[y])),delete l[y],g[y]!==void 0&&(l[y]=St(g[y]));return{base:d,proposed:l,warnings:s}}let u=Rn(i).get(n),p=Rn(d).get(n),f=Rn(l).get(n);if(p&&f&&u){let m=r==="proposed"?ri(p.node,f.node,u.node,s):f.node;ii(l,n,{...f,node:wr(p.node,m,u.node,r).value})}else r==="live"&&ii(l,n,u?{...u,node:St(u.node)}:void 0);return ii(d,n,u?{...u,node:St(u.node)}:void 0),{base:d,proposed:l,warnings:s}}function ii(e,t,n){let r=ye(e.nodes),o=ye(e.configs),s=o.some(l=>l.id===t),i=r.some(l=>l.id===t);if(e.nodes=r.filter(l=>l.id!==t),Array.isArray(e.configs)&&(e.configs=o.filter(l=>l.id!==t)),!n)return;s||!i&&n.config?e.configs=[...ye(e.configs),n.node]:e.nodes=[...e.nodes,n.node]}function St(e){return e===void 0?e:JSON.parse(JSON.stringify(e))}var ld,ai,Af,xf,cd,ud,Sf,Pn=q(()=>{"use strict";ld=require("crypto");pn();Pe();ai=["label","disabled","info","env"],Af=["func","libs","initialize","finalize","outputs"],xf=new Set(["x","y"]),cd={label:"",disabled:!1,info:"",env:[]},ud=new Set(["z"]),Sf=200;a(qt,"stableStringify");a(Tf,"hashTab");a(ye,"asNodes");a(Rn,"mapTab");a(Ze,"tabProps");a(Ao,"changedFields");a(oi,"envMap");a(Tn,"isObject");a(pd,"changedFieldPaths");a(wr,"mergeFields");a(xt,"nodeLabel");a(fd,"liveTabFromFlows");a(zt,"nestedLiveTab");a(xo,"containsNewPlaceholder");a(hd,"rebaseFlowTab");a(Rf,"finish");a(br,"hashFlowTab");a(Pf,"normalizeForHash");a(md,"withoutZ");a(si,"dedupe");a(gd,"applyConflictChoice");a(ii,"setNode");a(St,"clone")});function yd(e){let t=new Map;for(let n of e.nodes??[])t.set(n.id,n);for(let n of e.configs??[])t.set(n.id,n);return t}function wd(e,t){return pd(e,t).filter(n=>!_f.has(n))}function bd(e){let t=yd(e.baseFlow??{}),n=yd(e.payload.flow),r=new Map;for(let[d,l]of n){let c=t.get(d);if(!c){r.set(d,new Set(["*"]));continue}let u=wd(c,l);u.length>0&&r.set(d,new Set(u))}for(let[d]of t)n.has(d)||r.set(d,new Set(["*"]));let o=Object.fromEntries(["label","disabled","info","env"].map(d=>[d,e.baseFlow?.[d]])),s=Object.fromEntries(["label","disabled","info","env"].map(d=>[d,e.payload.flow[d]])),i=wd(o,s);return i.length>0&&r.set(e.payload.flowId,new Set(i)),r}function Cf(e,t){let n=[],r=[];for(let[o,s]of e){let i=t.get(o);if(!i)continue;let d=[];for(let c of s){if(c==="*"){d.push(...[...i].filter(u=>u!=="*")),i.has("*")&&d.push("*");continue}(i.has("*")||[...i].some(u=>u===c||u.startsWith(`${c}.`)||c.startsWith(`${u}.`)||u.startsWith(`${c}[`)||c.startsWith(`${u}[`)))&&d.push(c)}let l=[...new Set(d)].sort();l.length>0&&(n.push(o),r.push({nodeId:o,fields:l}))}return n.sort(),{nodeIds:n,fields:r}}function Qe(e,t,n){let r=bd(t),o=[];for(let s of e.list()){if(s.id===t.id||!U(s.status)||s.payload.flowId!==t.payload.flowId)continue;let i=Js(s),d=n?dt(s.createdBy)===n:dt(s.createdBy)===dt(t.createdBy),l=Cf(r,bd(s));if(l.nodeIds.length===0)continue;let c={id:s.id,title:i,displayName:Ks(i,s.createdAt),mine:d};l.nodeIds.length>0&&(c.overlappingNodeIds=l.nodeIds),l.fields.length>0&&(c.overlappingFields=l.fields),o.push(c)}return{conflictingProposals:o}}function et(e){let t={};return e.conflictingProposals.length>0&&(t.conflictingProposals=e.conflictingProposals,t.warning="Other pending proposals touch the same flow \u2014 approve order may conflict (like a git merge)."),t}function pe(e){return e>0?{autoClosedCount:e}:{}}var _f,Wt=q(()=>{"use strict";Ve();Oe();Pn();_f=new Set(["x","y","id","z"]);a(yd,"collectNodes");a(wd,"changedFieldKeys");a(bd,"computeProposalTouches");a(Cf,"overlapBetween");a(Qe,"findConflictingProposals");a(et,"conflictWarningFields");a(pe,"autoClosedFields")});function xd(e,t){Ad=e,So=t??(()=>Date.now())}function Sd(){let e=Ad();return(Number.isFinite(e)&&e>=1?e:vd)*kd}function Ef(e){return`Proposal expired: it stayed open longer than the proposal TTL (${e} day${e===1?"":"s"}). Ask the AI to resubmit if still needed.`}function To(e,t=So()){let n=Date.parse(e.createdAt);return Number.isFinite(n)&&t-n>Sd()}function te(e,t={}){let n=So(),r=Math.round(Sd()/kd),o=0;for(let s of e.list())if(U(s.status)&&!(t.flowId&&s.payload.flowId!==t.flowId)&&!e.isApproving(s.id)&&To(s,n))try{e.markClosed(s.id,"expired",Ef(r)),o+=1}catch{}return o}function di(e,t){let n=hd({flowId:e.payload.flowId,base:e.baseFlow,proposed:e.payload.flow,liveFlows:t.flows,liveRev:t.rev,baseRev:e.expectedRev});return n.state==="clean"&&e.aiConflictOverrides?.length&&(n.state="merged"),n}function li(e,t=So()){let n={rev:e.rev,state:e.state,conflicts:e.conflicts.map(r=>({...r,choices:[...r.choices]})),liveChangedCount:e.liveChangedIds.length,checkedAt:new Date(t).toISOString()};return e.mergedHash&&(n.mergedHash=e.mergedHash),n}function Me(e,t,n,r={}){let o=di(t,n);return{proposal:e.isApproving(t.id)&&!r.duringApprove?e.get(t.id):e.recordRebase(t.id,li(o)),result:o}}async function vr(e,t,n={}){let r=te(e,n.flowId?{flowId:n.flowId}:{}),o=n.live;if(!o&&t)try{o=await $e(t).getFlows()}catch{}if(!o)return{expired:r,conflicts:0,checked:0};let s=De(e,o,n);return{...s,expired:r+s.expired}}function De(e,t,n={}){let r={expired:0,conflicts:0,checked:0};for(let o of e.list())if(U(o.status)&&!(n.flowId&&o.payload.flowId!==n.flowId)&&!e.isApproving(o.id))try{if(To(o)){r.expired+=te(e,{flowId:o.payload.flowId});continue}let{proposal:s}=Me(e,o,t);r.checked+=1,s.status==="conflict"&&(r.conflicts+=1)}catch{}return r}function Td(e,t=500){let n,r,o=!1,s=a(async()=>{if(r)return o=!0,r;r=(async()=>{do{o=!1;try{await e()}catch{}}while(o)})();try{await r}finally{r=void 0}},"exec");return{trigger(){n&&clearTimeout(n),n=setTimeout(()=>{n=void 0,s()},t),n.unref?.()},async flush(){n&&(clearTimeout(n),n=void 0),await s()},cancel(){n&&clearTimeout(n),n=void 0}}}async function tt(e){if(e)try{return await $e(e).getFlows()}catch{return}}var vd,kd,Ad,So,nt=q(()=>{"use strict";Ve();Pn();Pe();vd=7,kd=864e5,Ad=a(()=>vd,"ttlDaysProvider"),So=a(()=>Date.now(),"nowProvider");a(xd,"configureProposalTtl");a(Sd,"proposalTtlMs");a(Ef,"ttlExpiredReason");a(To,"isPastTtl");a(te,"expireByTtl");a(di,"rebaseProposal");a(li,"rebaseInfoFrom");a(Me,"syncOneProposal");a(vr,"syncOpenProposals");a(De,"rebaseOpenProposalsWith");a(Td,"createDebouncedSync");a(tt,"tryRawLive")});var Nd={};Qi(Nd,{applyFlowNodeOps:()=>Gt,applyReplaceTextToValue:()=>Ld,buildPatchedFlowTab:()=>kr,collectSetProps:()=>ci,decodeBase64UrlJson:()=>Ar,findTextOccurrences:()=>Id,interpretPatch:()=>xr});function Ed(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function If(e){if(typeof e!="object"||e===null||Array.isArray(e))throw new k(`add op needs node as an object (got ${Ed(e)})`,"invalid_node")}async function kr(e,t,n){return Gt(e,t,n.map(r=>({op:"set",nodeId:r.nodeId,set:r.set})))}function Id(e,t){if(!t)return[];let n=[],r=0;for(;r<=e.length;){let o=e.indexOf(t,r);if(o<0)break;n.push(o),r=o+t.length}return n}function Ld(e,t,n,r,o){if(Cd.has(t))throw new k(`Cannot change protected node property '${t}' via replace_text`,"protected_property");if(typeof n!="string"||n.length===0)throw new k("find must be a non-empty string","missing_find");if(typeof r!="string")throw new k("replace must be a string","invalid_replace");if(e===void 0)throw new k(`Field '${t}' was not found on the node`,"field_not_found");if(typeof e!="string")throw new k(`Field '${t}' is not a string (got ${e===null?"null":typeof e})`,"field_not_string");let s=Id(e,n);if(s.length===0)throw new k(`Text not found in field '${t}'`,"text_not_found",{details:{matchCount:0}});let i=o;if(i===void 0){if(s.length>1)throw new k(`Found ${s.length} matches in '${t}'; pass occurrence (1-based) to pick one`,"ambiguous_match",{statusCode:409,details:{matchCount:s.length}});i=1}if(!Number.isInteger(i)||i<1||i>s.length)throw new k(`occurrence must be an integer from 1 to ${s.length}`,"invalid_occurrence",{details:{matchCount:s.length}});let d=s[i-1];if(d===void 0)throw new k(`occurrence must be an integer from 1 to ${s.length}`,"invalid_occurrence",{details:{matchCount:s.length}});return e.slice(0,d)+r+e.slice(d+n.length)}function Lf(e){let t=[];for(let n of e)if(n.op==="add"){let r=n.node;if(typeof r!="object"||r===null||Array.isArray(r)){t.push({code:"invalid_node",error:`add op needs node as an object (got ${Ed(r)})`});continue}let o=r;(!o.id||typeof o.id!="string")&&t.push({code:"missing_node_id",error:"add op needs node.id"});try{bt(o.type,"node.type")}catch(s){s instanceof k&&t.push({code:s.code,error:s.message,nodeId:o.id})}"wires"in o&&kn(o.wires,t,typeof o.id=="string"?o.id:void 0)}else n.op==="set"&&typeof n.set=="object"&&"wires"in n.set&&kn(n.set.wires,t,n.nodeId);return t}function Nf(e){let t=new Set;for(let n of e)n.op==="add"&&typeof n.node.id=="string"?t.add(n.node.id):n.op!=="add"&&typeof n.nodeId=="string"&&t.add(n.nodeId);return t}async function Gt(e,t,n,r={}){if(!t)throw new k("flow id is required","missing_flow_id");if(!n.length)throw new k("At least one node edit is required","missing_edits");let o=Lf(n);o.push(...Hs([n.flatMap(h=>h.op==="add"?[h.node]:[])],"the batch (add ops)")),lt(o);let s=await e.getFlow(t);if(r.expectedRev!==void 0&&s.rev!==r.expectedRev){let h=Array.isArray(s.flow.nodes)?[...s.flow.nodes]:[];return{expectedRev:s.rev,flow:{...s.flow,id:s.flow.id,type:s.flow.type??"tab",nodes:h}}}let i=Array.isArray(s.flow.nodes)?[...s.flow.nodes]:[],d=Array.isArray(s.flow.configs)?[...s.flow.configs]:[],l={...s.flow,id:s.flow.id,type:s.flow.type??"tab",nodes:i,configs:d},c=i.map(h=>({...h})),u=[];for(let h of n)try{h.op==="add"&&r.nodeTypes&&await $a(r.nodeTypes,h.node.type),Pd(c,t,h)}catch(g){if(g instanceof k)u.push({code:g.code,error:g.message,...g.statusCode!==400?{statusCode:g.statusCode}:{},...g.details??{}});else throw g}u.push(...Us(c,t));let p=Nf(n),f=await js(c,r.nodeTypes,{softWarningNodeIds:p});u.push(...f.errors),lt(u);for(let h of n)Pd(i,t,h);let m=[...f.warnings];if(r.nodeTypes){let h=await Bs(i,d,r.nodeTypes);m.push(...h.filter(g=>!g.nodeId||p.has(g.nodeId)))}return{expectedRev:s.rev,flow:l,...m.length>0?{warnings:m}:{}}}function Rd(e,t,n){if(!t||typeof t!="string")throw new k("Each edit needs a node id","missing_node_id");let r=e.findIndex(o=>o.id===t);if(r<0||!e[r])throw new k(`Node '${t}' was not found in flow '${n}'`,"node_not_found",{statusCode:404});return r}function Pd(e,t,n){if(n.op==="add"){let i=n.node;if(!i.id||typeof i.id!="string")throw new k("add op needs node.id","missing_node_id");if(bt(i.type,"node.type"),e.some(d=>d.id===i.id))throw new k(`Node '${i.id}' already exists in flow '${t}'`,"node_already_exists");e.push({...i,id:i.id,type:i.type,z:typeof i.z=="string"&&i.z?i.z:t});return}if(n.op==="remove"){let i=Rd(e,n.nodeId,t);e.splice(i,1);return}let r=Rd(e,n.nodeId,t),o=e[r];if(!o)throw new k(`Node '${n.nodeId}' was not found in flow '${t}'`,"node_not_found",{statusCode:404});if(n.op==="replace_text"){if(!(n.field in o))throw new k(`Field '${n.field}' was not found on the node`,"field_not_found");let i=Ld(o[n.field],n.field,n.find,n.replace,n.occurrence);e[r]={...o,id:o.id,type:o.type,[n.field]:i};return}let s={...o,id:o.id,type:o.type};for(let[i,d]of Object.entries(n.set)){if(Cd.has(i))throw new k(`Cannot change protected node property '${i}' via GET propose`,"protected_property");if(i==="wires"){let l=[];kn(d,l,n.nodeId),lt(l)}s[i]=d}e[r]=s}function _d(e){if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean"||typeof e=="bigint")return String(e)}function ci(e){let t={},n=e.set;if(n&&typeof n=="object"&&!Array.isArray(n))for(let[r,o]of Object.entries(n)){let s=_d(o);s!==void 0&&(t[r]=s)}for(let[r,o]of Object.entries(e)){if(!r.startsWith("set.")||r.length<=4)continue;let s=r.slice(4);if(typeof o=="string")t[s]=o;else if(Array.isArray(o)&&typeof o[0]=="string")t[s]=o[0];else{let i=_d(o);i!==void 0&&(t[s]=i)}}return t}function Ar(e){let t;try{t=Buffer.from(e,"base64url").toString("utf8")}catch{throw new k("patch must be base64url-encoded JSON","invalid_patch")}try{return JSON.parse(t)}catch{throw new k("patch JSON is malformed","invalid_patch_json")}}function Ff(e,t){if(typeof e.flowId=="string"&&e.flowId)return e.flowId;let n=e.flow;if(n&&typeof n=="object"&&!Array.isArray(n)){let r=n.id;if(typeof r=="string"&&r)return r}return t}function ui(e){return typeof e.id=="string"&&e.id?e.id:typeof e.nodeId=="string"&&e.nodeId?e.nodeId:typeof e.node=="string"&&e.node?e.node:""}function Of(e){if(e==null||e==="")return;let t;if(typeof e=="number")t=e;else if(typeof e=="string")t=Number(e);else if(typeof e=="boolean"||typeof e=="bigint")t=Number(e);else throw new k("occurrence must be a positive integer","invalid_occurrence");if(!Number.isFinite(t))throw new k("occurrence must be a positive integer","invalid_occurrence");return Math.trunc(t)}function $f(e){let t=e.node;if(t!==void 0)return If(t),{op:"add",node:t};let n={...e};return delete n.op,{op:"add",node:n}}function Mf(e){let t=ui(e);if(!t)throw new k("replace_text op needs node id","missing_node_id");let n=typeof e.field=="string"?e.field:"";if(!n)throw new k("replace_text op needs field","missing_field");if(typeof e.find!="string")throw new k("replace_text op needs find string","missing_find");if(typeof e.replace!="string")throw new k("replace_text op needs replace string","invalid_replace");let r=Of(e.occurrence),o={op:"replace_text",nodeId:t,field:n,find:e.find,replace:e.replace};return r!==void 0&&(o.occurrence=r),o}function Df(e){let t=ui(e);if(!t)throw new k("Each patch.nodes entry needs id","missing_node_id");let n=e.set,r={};if(n&&typeof n=="object"&&!Array.isArray(n))Object.assign(r,n);else for(let[o,s]of Object.entries(e))o==="id"||o==="nodeId"||o==="node"||o==="set"||o==="op"||(r[o]=s);return{op:"set",nodeId:t,set:r}}function Hf(e){let t=typeof e.op=="string"?e.op.toLowerCase():"set";if(t==="add")return $f(e);if(t==="remove"){let n=ui(e);if(!n)throw new k("remove op needs id","missing_node_id");return{op:"remove",nodeId:n}}if(t==="replace_text")return Mf(e);if(t!=="set")throw new k("nodes[].op must be 'add', 'remove', 'set', or 'replace_text'","invalid_patch");return Df(e)}function xr(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new k("patch must be a JSON object","invalid_patch");let n=e;if(n.type==="replace_flow"||n.flow&&typeof n.flow=="object"){let r=Ff(n,t);if(!r)throw new k("patch.replace_flow needs flowId","missing_flow_id");return{kind:"replace_flow",flowId:r,flow:n.flow}}if(Array.isArray(n.nodes)){let r=typeof n.flowId=="string"?n.flowId:t;if(!r)throw new k("patch.nodes needs flow id","missing_flow_id");let o=[];for(let i of n.nodes){if(!i||typeof i!="object"||Array.isArray(i))throw new k("Each patch.nodes entry must be an object","invalid_patch");o.push(Hf(i))}let s=o.filter(i=>i.op==="set").map(i=>({nodeId:i.nodeId,set:i.set}));return{kind:"nodes",flowId:r,ops:o,edits:s}}throw new k("patch must include nodes[] edits or type/flow for replace_flow","invalid_patch")}var Cd,Ro=q(()=>{"use strict";se();qs();Ut();Cd=new Set(["id","type","z"]);a(Ed,"describeNonObjectNode");a(If,"assertAddNodeObject");a(kr,"buildPatchedFlowTab");a(Id,"findTextOccurrences");a(Ld,"applyReplaceTextToValue");a(Lf,"collectOpShapeIssues");a(Nf,"touchedNodeIdsFromOps");a(Gt,"applyFlowNodeOps");a(Rd,"requireNodeIndex");a(Pd,"applyOneNodeOp");a(_d,"stringifyQueryValue");a(ci,"collectSetProps");a(Ar,"decodeBase64UrlJson");a(Ff,"resolveReplaceFlowId");a(ui,"resolveNodeId");a(Of,"parseOccurrence");a($f,"parseAddOp");a(Mf,"parseReplaceTextOp");a(Df,"parseSetOp");a(Hf,"parseNodeOp");a(xr,"interpretPatch")});function Kt(e,t){return dt(e.createdBy)===t.id}function fi(e,t){return!t||t.length===0||t.includes(e)}function jf(e){return typeof e=="string"&&Od.has(e)}function Cn(e,t){let n=Uf[e],r=e!=="status"&&Array.isArray(t);return new k(r?`Invalid ${e} filter: pass it at most once (multiple values are not supported). Allowed: ${n}`:`Invalid ${e} filter. Allowed: ${n}`,"invalid_filter",{details:{parameter:e,allowed:n,hint:`Pass ${e} as ${n}, or omit it.`}})}function Po(e,t){if(Array.isArray(t))throw Cn(e,t);return t}function Bf(e){return typeof e=="number"?Number.isInteger(e)?e:null:typeof e=="string"&&/^-?\d+$/.test(e.trim())?Number(e.trim()):null}function hi(e){return e==null||e===""}function qf(e){if(!hi(e)){if(e==="true"||e==="1"||e===!0)return!0;if(e==="false"||e==="0"||e===!1)return!1;throw Cn("mine",e)}}function mi(e){if(hi(e))return;let t=(Array.isArray(e)?e:[e]).flatMap(r=>typeof r=="string"?r.split(","):[r]),n=[];for(let r of t){let o=typeof r=="string"?r.trim():r;if(o!==""){if(!jf(o))throw Cn("status",e);n.includes(o)||n.push(o)}}return n.length>0?n:void 0}function Fd(e,t,n,r){if(hi(t))return;let o=Bf(t);if(o===null||o<n||o>r)throw Cn(e,t);return o}function _o(e){let t={},n=qf(Po("mine",e.mine));n!==void 0&&(t.mine=n);let r=mi(e.status);r&&(t.status=r);let o=Po("flowId",e.flowId??e.flow_id??e.flow);typeof o=="string"&&o.length>0&&(t.flowId=o);let s=Fd("limit",Po("limit",e.limit),1,_n);s!==void 0&&(t.limit=s);let i=Fd("offset",Po("offset",e.offset),0,Number.MAX_SAFE_INTEGER);return i!==void 0&&(t.offset=i),t}function Jt(e){let t=e.offset+e.returned,n=t<e.total;return{total:e.total,offset:e.offset,...e.limit!==null?{limit:e.limit}:{},hasMore:n,...n?{nextOffset:t}:{}}}function zf(e,t){let n=e.length,r=t.offset!==void 0&&t.offset>0?t.offset:0;if(t.limit===void 0)return{items:r>0?e.slice(r):e,total:n,limit:null,offset:r};let o=Math.min(Math.max(1,t.limit),_n);return{items:e.slice(r,r+o),total:n,limit:o,offset:r}}function En(e,t,n={},r={}){let o=r.mine===!0?t.id:void 0,s={};o&&(s.linkId=o),r.flowId&&(s.flowId=r.flowId);let d=e.list(s).filter(c=>r.mine!==!1||!Kt(c,t)).filter(c=>fi(c.status,r.status)).map(c=>({...ue(Ye(c),n),mine:Kt(c,t)})),l=zf(d,r);return{proposals:l.items,total:l.total,limit:l.limit,offset:l.offset}}function ut(e,t,n,r={}){if(!n)throw new k("Missing proposal id","missing_proposal_id");let o=e.get(n);return{...ue(Ye(o),r),mine:Kt(o,t)}}function Wf(e){let t={kind:"access-link",linkId:e.id};return typeof e.label=="string"&&e.label&&(t.label=e.label),t}function In(e,t,n,r,o,s={}){if(!r)throw new k("Missing proposal id","missing_proposal_id");let i;try{i=e.get(r)}catch(d){throw d instanceof me,d}if(!Kt(i,n))throw new wt(r);if(!U(i.status))throw new Y(r,i.status);return{...gi(e,t,Wf(n),r,o,s),mine:!0}}function gi(e,t,n,r,o,s={}){let i=e.get(r);if(n.kind==="access-link"&&dt(i.createdBy)!==n.linkId)throw new wt(r);let d=typeof o=="string"&&o.trim()?o.replace(/\0/g,"").trim().slice(0,2e3):n.kind==="admin"?"Withdrawn by administrator (token recovery)":"Withdrawn by access link",l=e.markWithdrawn(r,n,d),c=ue(Ye(l),s);return t.append("proposal.withdrawn",n,{proposalId:r,flowId:i.payload.flowId,title:c.title,displayName:c.displayName,reason:d,withdrawalAuthority:n.kind==="admin"?"admin-recovery":"owner-token",originalOwnerId:dt(i.createdBy),...c.flowLabel?{flowLabel:c.flowLabel}:{}}),c}function Gf(e,t,n,r={}){let o;try{o=e.get(t.id)}catch{return{}}if(r.live){De(e,r.live,{flowId:o.payload.flowId});try{o=e.get(t.id)}catch{return{}}}return U(o.status)?et(Qe(e,o,n)):{}}function he(e,t,n,r={},o={}){let s="mine"in n?n:{...ue(n,r),mine:Kt(n,t)},i=0;if(o.live)try{let c=e.get(s.id).payload.flowId;i=De(e,o.live,{flowId:c}).expired;let u=e.get(s.id);s={...ue(u,r),mine:Kt(u,t)}}catch{}let d=ie(Ye(s),r),l={...d,mine:s.mine,...pe(i)};return o.includeConflicts!==!1&&U(d.status)&&Object.assign(l,Gf(e,d,t.id)),l}async function pt(e){let t=await e.getFlows();return{rev:t.rev,flows:t.flows}}async function He(e,t){if(!t)throw new k("Missing flow id","missing_flow_id");let n=await e.getFlow(t);return{rev:n.rev,flow:vn(t,n.flow)}}function Kf(e){if(typeof e.expectedRev=="string"&&e.expectedRev.length>0)return e.expectedRev;if(typeof e.expected_rev=="string"&&e.expected_rev.length>0)return e.expected_rev}function $d(e,t){if("nodes"in e&&e.nodes!==void 0&&!Array.isArray(e.nodes))throw new k("payload.nodes must be an array of node ops","invalid_nodes");if(t!==void 0&&!Array.isArray(t))throw new k("nodes must be an array of node ops","invalid_nodes")}async function Jf(e,t,n,r,o){let{applyFlowNodeOps:s,interpretPatch:i}=await Promise.resolve().then(()=>(Ro(),Nd));$d(n,r);let d=Array.isArray(n.nodes)?n.nodes:Array.isArray(r)?r:null;if(!d)throw new k("payload.flow is required","missing_flow");let l=n.flowId,c=typeof l=="string"?l:"";if(!c)throw new k("payload.flowId is required","missing_flow_id");let u=i({nodes:d,flowId:c},c);if(u.kind!=="nodes")throw new k("payload.nodes must be node ops","invalid_patch");let p=await s(e,u.flowId,u.ops,{expectedRev:t,...o?{nodeTypes:o}:{}});if(p.expectedRev!==t)throw new Q(t,p.expectedRev,"submit_proposal");return{flowId:u.flowId,flow:p.flow}}async function Vf(e,t,n,r,o){let s=n.flowId,i=n.flow,d=!i||typeof i!="object"||Array.isArray(i);if(d&&$d(n,r),d&&(Array.isArray(n.nodes)||Array.isArray(r))){let l=await Jf(e,t,n,r,o);s=l.flowId,i=l.flow}if(typeof s!="string"||s.length===0)throw new k("payload.flowId is required","missing_flow_id");if(!i||typeof i!="object"||Array.isArray(i))throw new k("payload.flow is required","missing_flow");return{flowId:s,flow:i}}function Yf(e,t){try{return{safeExpectedRev:Ge(e.slice(0,200),"expectedRev"),safeFlowId:Ge(t.slice(0,200),"flowId")}}catch{throw new k("expectedRev and payload.flowId must be safe path segments","invalid_path_segment")}}function Xf(e,t){let n=sd(Ne(e),t);return n.unresolved.length>0&&lt(n.unresolved.map(r=>({code:"redacted_placeholder_mismatch",error:`Node ${r.nodeId} field ${r.property}: ${r.reason}. [REDACTED] placeholders are restored only when left exactly where they were shown (same node, field, env/header name, and unchanged line). Do not copy, move, or add placeholders; keep those lines unchanged or supply a real new value.`,nodeId:r.nodeId,property:r.property,statusCode:400}))),n}async function Md(e,t,n){let r=e.type,o=Kf(e),s=e.payload;if(r!=="replace_flow")throw new k("type must be 'replace_flow'","invalid_proposal_type");if(!o)throw new k("expectedRev is required (top-level field next to type and payload)","missing_expected_rev");if(!s||typeof s!="object"||Array.isArray(s))throw new k("payload must be an object","invalid_payload");let i=s,d=$e(t),l=await d.getFlows(),c=co(l,d.kind);if(l.rev!==o){let T=Cs(d)?.get(o);if(!T)throw new Q(o,l.rev,"submit_proposal");c=co(T,d.kind)}let u=await Vf(At(c),o,i,e.nodes,n),{safeExpectedRev:p,safeFlowId:f}=Yf(o,u.flowId),m=u.flow,h=Buffer.byteLength(JSON.stringify(s),"utf8");if(h>K)throw new ce("proposal payload",h,K);lt(Ha(m,f));let y=await c.getFlows();if(y.rev!==p)throw new Q(p,y.rev,"submit_proposal");let w=Xf(m,y.flows),v=Ne(w.flow);lt(Da(v,f,y.flows));let L=await c.getFlow(f);if(L.rev!==p)throw new Q(p,L.rev,"submit_proposal");let P=Ne(L.flow);n&&await Oa(n,v);let{errors:I,warnings:D}=await ja(v,{flowId:f,baseFlow:P,...n?{nodeTypes:n}:{}});return lt(I),D.unshift(...yr(P,v).map(T=>({...T})),...dd(w.restoredFields,v,y.flows).map(T=>({...T}))),{safeExpectedRev:p,safeFlowId:f,safeFlow:v,safeBaseFlow:P,restoration:w,flowWarnings:D,head:l}}function pi(e){let t={kind:"access-link",linkId:e.id};return e.label!==void 0&&(t.label=e.label),t}async function Tt(e,t,n,r,o,s={},i){let d=await Md(r,o,i),{safeExpectedRev:l,safeFlowId:c,safeFlow:u,safeBaseFlow:p,restoration:f,flowWarnings:m}=d,h=pi(n),g=n.oauthClientId&&n.oauthUser?{kind:"oauth",clientId:n.oauthClientId,user:n.oauthUser}:h,y=ur(r.title)??Gs(p,u),w=e.create({type:"replace_flow",expectedRev:l,payload:{flowId:c,flow:u},baseFlow:p,...f.restoredFields.length>0?{restoredSecrets:f.restoredFields}:{},title:y,createdBy:g,...m.length>0?{warnings:m}:{}}),v=Me(e,w,d.head).proposal,L=ue(Ye(v),s),P=L.flowLabel||void 0;return t.append("proposal.created",h,{proposalId:v.id,flowId:c,expectedRev:l,...d.head.rev!==l?{liveRev:d.head.rev}:{},...v.rebase?{rebaseState:v.rebase.state}:{},title:L.title,displayName:L.displayName,...P?{flowLabel:P}:{},...m.length>0?{warningCount:m.length}:{},...f.restored>0?{restoredRedactions:f.restored}:{}}),L}async function Co(e,t,n,r,o,s,i={},d){if(!r)throw new k("Missing proposal id","missing_proposal_id");let l=e.get(r);if(!Kt(l,n))throw new wt(r);if(!U(l.status))throw new Y(r,l.status);if(o.choice!==void 0||o.nodeId!==void 0){if(o.payload!==void 0||o.nodes!==void 0||o.expectedRev!==void 0||o.title!==void 0)throw new k("Use nodeId + choice alone, or expectedRev + payload to replace the body; do not mix the modes.","invalid_choice");let h=await yi(e,t,pi(n),r,o.nodeId,o.choice,s);return{...ue(Ye(h),i),mine:!0}}let c=await Md({type:"replace_flow",...o,payload:o.payload??{flowId:l.payload.flowId}},s,d);if(c.safeFlowId!==l.payload.flowId)throw new k(`payload.flowId must stay '${l.payload.flowId}' (submit a new proposal for another flow)`,"flow_id_mismatch");let u=ur(o.title),p=e.updateBody(r,{baseFlow:c.safeBaseFlow,flow:c.safeFlow,expectedRev:c.safeExpectedRev,restoredSecrets:c.restoration.restoredFields,warnings:c.flowWarnings,...u?{title:u}:{}}),f=Me(e,p,c.head).proposal,m=ue(Ye(f),i);return t.append("proposal.resolved",pi(n),{proposalId:r,flowId:f.payload.flowId,expectedRev:c.safeExpectedRev,...c.restoration.restored>0?{restoredRedactions:c.restoration.restored}:{},title:m.title,displayName:m.displayName,...f.rebase?{rebaseState:f.rebase.state}:{}}),{...m,mine:!0}}async function yi(e,t,n,r,o,s,i){if(s!=="live"&&s!=="proposed")throw new k("choice must be 'live' or 'proposed'","invalid_choice");if(typeof o!="string"||o.length===0||o.length>200)throw new k("nodeId is required","missing_node_id");let d=await $e(i).getFlows(),l=e.get(r);if(!U(l.status))throw new Y(r,l.status);let c=Me(e,l,d).proposal,u=c.rebase?.conflicts.find(h=>h.nodeId===o);if(!u)throw new k("No current conflict for that node (it may already be resolved). Reload the proposal.","conflict_not_found",{statusCode:409});if(!u.choices.includes(s))throw new k("That choice is not available for this conflict.","invalid_choice");let p;try{p=gd({flowId:c.payload.flowId,base:c.baseFlow,proposed:c.payload.flow,liveFlows:d.flows,nodeId:o,choice:s})}catch(h){throw h instanceof mr?new Je(r,c.rebase?.conflicts??[]):new k("This conflict cannot be resolved by hand.","invalid_choice")}let f=e.updateBody(r,{baseFlow:Ne(p.base),flow:Ne(p.proposed),conflictChoice:{nodeId:o,choice:s,chooser:n.kind==="admin"?"human":"ai",undo:{baseFlow:c.baseFlow??{},flow:c.payload.flow,warnings:c.warnings??[],aiConflictOverrides:c.aiConflictOverrides??[]}},aiConflictOverrides:[...(c.aiConflictOverrides??[]).filter(h=>h.nodeId!==o),...n.kind!=="admin"&&s==="proposed"?[{nodeId:o,fields:u.fields?.length?u.fields:[""]}]:[]],warnings:[...(c.warnings??[]).filter(h=>h.code!=="secret_may_revert"||h.nodeId!==o),...p.warnings]}),m=Me(e,f,d).proposal;return t.append("proposal.conflict_resolved",n,{proposalId:r,flowId:m.payload.flowId,nodeId:o,choice:s,kind:u.kind,chooser:n.kind==="admin"?"human":"ai",status:m.status}),m}async function Sr(e,t){let n=$e(e),r;try{r=(await n.getFlows()).rev}catch{return e}if(r===t)return e;let o=Cs(n)?.get(t);if(!o)return e;let s=co(o,n.kind);return e===n?s:At(s)}function W(e,t="rest"){return Tr(e,t)}var _n,Od,Uf,k,se=q(()=>{"use strict";Te();ao();Es();Ve();no();oe();rt();qs();Ut();Wt();Oe();Pn();nt();Pe();a(Kt,"isMineProposal");_n=200,Od=new Set(["pending","conflict","conflict","approved","rejected","failed","withdrawn","expired","superseded"]);a(fi,"matchesStatusFilter");a(jf,"isProposalStatus");Uf={status:[...Od].join(", "),limit:`integer 1\u2013${_n}`,offset:"integer \u2265 0",mine:"true, false, 1, 0",flowId:"one flow tab id"};a(Cn,"invalidFilterError");a(Po,"singleQueryValue");a(Bf,"parseIntegerFilter");a(hi,"isUnset");a(qf,"parseMineFilter");a(mi,"parseStatusFilter");a(Fd,"parseRangeFilter");a(_o,"parseListProposalsQuery");a(Jt,"listPageFields");a(zf,"applyListPagination");a(En,"listAccessibleProposals");a(ut,"getAccessibleProposal");a(Wf,"accessLinkActor");a(In,"withdrawMyProposal");a(gi,"withdrawProposalAsActor");a(Gf,"conflictsForProposal");a(he,"presentAccessibleProposalForApi");k=class extends Error{static{a(this,"AiOpsValidationError")}statusCode;code;details;constructor(t,n="validation_error",r={}){super(t),this.name="AiOpsValidationError",this.code=n,this.statusCode=r.statusCode??400,r.details&&(this.details=r.details)}};a(pt,"listFlows");a(He,"getFlow");a(Kf,"resolveExpectedRev");a($d,"assertNodesArrayOrAbsent");a(Jf,"materializePayloadFromNodes");a(Vf,"resolveReplaceFlowPayload");a(Yf,"assertSafeRevAndFlowId");a(Xf,"strictRestoreOrThrow");a(Md,"prepareReplaceFlowSubmission");a(pi,"auditActorFor");a(Tt,"submitReplaceFlowProposal");a(Co,"resolveMyProposal");a(yi,"resolveConflictChoice");a(Sr,"adapterAtRev");a(W,"aiOpsErrorToHttp")});function wi(e){return e==="get_link"?86400:2592e3}function bi(e,t){return e==="get_link"?`get_link ttlSeconds must be \u2264 ${t} (24 hours)`:`ttlSeconds must be \u2264 ${t} (30 days) for ${e} tokens`}var Vt,Rr=q(()=>{"use strict";a(wi,"maxTtlSecondsForKind");a(bi,"ttlCapErrorMessage");Vt="ttlSeconds must be a positive number of seconds within the cap for the token kind: get_link \u2264 86400 (24h); rest and mcp \u2264 2592000 (30 days)."});function R(e,t,n,r,o=ne,s="error"){return{code:e,status:t,meaning:n,hint:r,channels:o,kind:s}}function Pr(e){return Qf.get(e)?.hint}function Dd(e){return e.replace(/\\/g,"\\\\").replace(/\|/g,"\\|")}function Hd(){let e=a(s=>`| \`${s.code}\` | ${s.status} | ${Dd(s.meaning)} | ${Dd(s.hint)} | ${s.channels.join(", ")} |`,"row"),t=["| Code | HTTP | Meaning | Next step | Channels |","| --- | --- | --- | --- | --- |"],n=Eo.filter(s=>s.kind==="error"),r=Eo.filter(s=>s.kind==="warning"),o=Eo.filter(s=>s.kind==="status");return[...t,...n.map(e),"","**Warnings** (in `warnings[]` on a successful propose; never blocking):","",...t,...r.map(e),"","**Proposal statuses.** Open proposals are closed automatically: `expired` after the configured TTL (default 7 days). Revision changes auto-rebase open proposals; `conflict` blocks approval until resolved. Legacy closed proposals stay closed. Submit, get, and list responses include `autoClosedCount` when that request auto-closed proposals (omitted when zero).","",...t,...o.map(e)].join(`
10
+ `)}var ne,Ln,Nn,Zf,Rt,Eo,Qf,Io=q(()=>{"use strict";Rr();ne=["GET","REST","MCP"],Ln=["GET"],Nn=["REST","MCP"],Zf=["GET","REST"],Rt=["admin"];a(R,"e");Eo=[R("duplicate_env_keys",200,"Changed env arrays contain duplicate names; pairing by name is ambiguous when entries are reordered","Review the env values before approval",[...ne,"admin"],"warning"),R("approve_queue_timeout",504,"Approval exceeded its queue-wait deadline (default 60 seconds); no deployment was started for this request","Check the live flow and retry approval",Rt),R("deploy_timeout",504,"Deployment exceeded its deadline (default 60 seconds); approvals wait for settlement or the hard cap (default 5 minutes)","Before the cap, late settlement updates the proposal; after the cap, deployPending.queueReleased is true and the outcome stays unknown. Check the live flow before approving again; approval always re-reads live",Rt),R("secret_may_revert",200,"Secret spans could not be aligned during proposed conflict resolution; proposed values were kept","Review the warned field against live flows before approval; an editor secret change may be reverted",[...ne,"admin"],"warning"),R("proposal_conflict",409,"Editor and proposal changes conflict (conflicts contains field names only)","Read the current flow and resolve the proposal before approval",[...ne,"admin"]),R("rebase_ack_required",409,"The merged result needs review (mergedHash included)","Review the diff and approve with its mergedHash",["admin"]),R("rebase_race",409,"Flows kept changing during approval","Recheck the proposal and retry approval",["admin"]),R("conflict_not_found",409,"The requested conflict no longer exists","Reload the proposal and its conflicts",[...Nn,"admin"]),R("invalid_choice",400,"Invalid manual conflict choice","Use nodeId plus an available live or proposed choice, without replacement payload or revision",[...Nn,"admin"]),R("flow_id_mismatch",400,"Resolution tried to change the proposal flow id","Keep the same flowId or submit a new proposal",Nn),R("access_link_missing",401,"No Bearer / path token","Ask the user for a new access link of the right kind"),R("bearer_scheme_required",401,"Auth used Basic (or non-Bearer)","Send `Authorization: Bearer <token>`",Nn),R("token_expired",401,"Known token past `expiresAt` (body includes `expiredAt` and `accessLinksUrl`)","Ask the user to create a new link on the `/ai-access` dashboard \u2192 Access tokens (`accessLinksUrl`)"),R("access_link_invalid",401,"Unknown, revoked, or cleared after restart","Ask the user for a new access link"),R("wrong_token_kind",403,"Token kind does not match this surface","Mint the matching kind on `/ai-access` \u2192 Access tokens; show `tellUser` to the human"),R("ai_features_disabled",403,"Plugin AI features off","Owner enables AI Access / `adminAuth`"),R("auth_required",401,"Admin session required","Sign in to the Node-RED editor",Rt),R("insufficient_permissions",403,"Admin lacks required scope","Need `*` for link mint / approve",Rt),R("invalid_link_kind",400,"Unknown `kind` on create","Use `get_link`, `rest`, or `mcp`",Rt),R("invalid_ttl",400,"`ttlSeconds` is zero, negative, not a number, or over the cap for the kind",Vt,Rt),R("access_link_limit",409,"Too many active links","Revoke an existing link first",Rt),R("access_link_not_found",404,"Unknown link id on revoke","List links and use an existing id",Rt),R("link_create_not_supported",403,"Attempted mint under a GET link","Admin mints via `POST /mcp/api/v1/links` only",Ln),R("rate_limited",429,"Per-IP or per-token limit","Wait `retryAfter` / `Retry-After`"),R("payload_too_large",413,"Body / proposal over ~1.5MB","Shrink content"),R("invalid_json",400,"Malformed JSON body","Fix Content-Type / body",Nn),R("unsupported_media_type",415,"POST body is not `application/json`","Send `Content-Type: application/json`",Nn),R("url_too_long",414,"GET URL over soft cap (~8KB)","Use chunked drafts or REST/MCP",Ln),R("not_found",404,"Unknown path or resource","Re-read docs / discovery"),R("missing_query",400,"Required search `q` missing","Pass `?q=`",Zf),R("invalid_filter",400,"Invalid list filter (`status`, `limit`, `offset`, `mine`); body names `parameter` and `allowed`","Use an allowed value: status one of the proposal statuses; limit integer 1\u2013200; offset integer \u2265 0; mine true|false"),R("invalid_arguments",400,"MCP tool arguments failed schema validation; body names `parameter`","Fix the named argument to match the tool input schema (see tools/list)",["MCP"]),R("invalid_diff_format",400,"Unknown `format` on proposal diff","Use `format=summary` (default) or `format=full`"),R("confirm_required",400,"GET withdraw without `confirm=1`","Repeat the request with `confirm=1`",Ln),R("missing_expected_rev",400,"Proposal submitted without `expectedRev` / `rev`","Read the flow and pass its `rev`"),R("version_mismatch",409,"Unknown or unavailable `rev` / `expectedRev` snapshot","Re-read flows and resubmit"),R("proposal_not_found",404,"Unknown proposal id","List proposals and use an existing id"),R("proposal_not_pending",409,"Action needs an open proposal","Only pending or conflict proposals can withdraw / decide"),R("proposal_not_owned",403,"Resolve or withdraw another token\u2019s proposal","Use the creating token; lost-token withdrawal requires a full admin session"),R("validation_failed",400,"Several validation errors at once (message `N validation errors`)","Fix every entry in errors[] and resubmit."),R("duplicate_node_id",400,"The same node id appears more than once in a full tab or batch","Give every node a unique id"),R("missing_node_id",400,"Node / op without an id","Include a non-empty string id"),R("missing_node_type",400,"Node without a `type`","Set `type` to an installed palette type name"),R("invalid_node_type",400,"`type` is not a string","Use the palette type name exactly"),R("invalid_nodes",400,"`flow.nodes` / `flow.configs` not an array","Send arrays (use `[]` to clear)"),R("invalid_node",400,"Node entry is not an object","Send `{ id, type, \u2026 }` objects"),R("invalid_wires",400,"Bad wires shape, missing target, or more ports than outputs","Wire only to existing node ids / ports"),R("z_mismatch",400,"Node `z` differs from the flow id","Set `z` to the tab id or omit it"),R("node_already_exists",400,"`add` op for an id already in the flow","Use `set` or a new id"),R("protected_property",400,"Tried to change id / type / z","Omit id, type, and z from edits"),R("node_not_found",404,"Unknown node (single error only; several errors \u2192 `validation_failed`)","Re-read the flow"),R("field_not_found",404,"Unknown field on a node","Re-read the node"),R("text_not_found",400,"`replace_text` find had no match","Re-read field; find is exact"),R("ambiguous_match",409,"`replace_text` matched more than once","Pass `occurrence` (1-based) or unique find"),R("unknown_node_type",400,"Proposal uses a type that is not installed","List node-types / palette search; ask user to install"),R("node_type_not_found",404,"Unknown type on node-types detail","List types or search palette"),R("chunk_conflict",409,"Same draft seq re-sent with different data","Re-send identical data or start a new draft",Ln),R("missing_chunks",400,"Draft submit with gaps in seq","Upload every seq from 0 through max",Ln),R("draft_hash_mismatch",400,"Draft sha256 does not match assembled bytes","Send the hex sha256 of the assembled UTF-8",Ln),R("missing_config_node",201,"Warning: node has no (or an unresolved) config node yet","Configure before deploy if needed; see `candidates`",ne,"warning"),R("unwired_node",201,"Warning: node has outputs but no wires","Wire it, or ignore while scaffolding",ne,"warning"),R("incomplete_wires",201,"Warning: fewer wire ports than outputs","Add ports if outputs should connect",ne,"warning"),R("redacted_placeholder_mismatch",400,"A `[REDACTED]` placeholder was added, duplicated, moved, or its env/header name changed","Keep placeholders exactly where they were shown (same line, same name) or supply a real value",ne),R("secret_destination_changed",201,"Warning: a node holding a restored secret changed where it sends data (url/host/method/headers/\u2026)","Reviewer must verify the new destination before approving",ne,"warning"),R("secret_node_code_changed",201,"Warning: a node holding a restored secret had its code changed","Reviewer must check the code does not send the secret elsewhere",ne,"warning"),R("secret_wires_changed",201,"Warning: a node holding a restored secret had its output wires changed","Reviewer must check where its messages now go",ne,"warning"),R("secret_scope_changed",201,"Warning: tab/group env or a config node scoping a secret-holding node changed","Reviewer must check env-resolved hosts/URLs (e.g. `${API_HOST}`)",ne,"warning"),R("outbound_node_changed",201,"Warning: proposal adds/changes an outbound-capable node (http request/response, mqtt, tcp, udp, email, websocket, link out/call, function, exec, file, or any node with url/host fields)","Reviewer must check it cannot send secret data elsewhere",ne,"warning"),R("secret_tab_changed",201,"Warning: proposal with restored secrets also changes other nodes in the tab","Review every change before approving",ne,"warning"),R("secret_reference_added",201,"Warning: an added/changed node now reads a secret-named env/global/flow value (env.get, $env, ${\u2026}, typed input)","Reviewer must check the value is not sent anywhere unexpected",ne,"warning"),R("conflict",200,"Status: open proposal has conflicting editor changes; approval blocked","Read the current flow and resolve the proposal",ne,"status"),R("expired",200,"Status: open proposal expired after the configured TTL (default 7 days)","Re-read the flow and submit a new proposal",ne,"status"),R("superseded",200,"Legacy closed status; new approvals rebase other open proposals","Re-read the flow and submit a new proposal if still needed",ne,"status"),R("upstream_error",502,"Node-RED runtime/flows error","Retry; check Node-RED logs"),R("internal_error",500,"Unexpected server error","Retry; check Node-RED logs")],Qf=new Map(Eo.map(e=>[e.code,e]));a(Pr,"registryHint");a(Dd,"cell");a(Hd,"renderGetLinkErrorTable")});function jd(e){let t=ue(e),n=go(e)||void 0;return{proposalId:e.id,title:t.title,displayName:t.displayName,flowId:e.payload.flowId,...n?{flowLabel:n}:{}}}function vi(e,t){return e!==void 0&&Number.isFinite(e)&&e>0?e:t}var Yt,Xt,Pt,Zt,Lo,eh,Ud,No,Fo=q(()=>{"use strict";Te();Ve();Oe();Pn();nt();Pe();a(jd,"auditProposalMeta");Yt=class extends Error{static{a(this,"ProposalRebaseAckRequiredError")}code="rebase_ack_required";statusCode=409;mergedHash;rebaseState;constructor(t,n,r){super(`Proposal '${t}' was rebased onto newer editor changes since you viewed it. Review the updated diff, then approve again with the new mergedHash.`),this.name="ProposalRebaseAckRequiredError",this.mergedHash=n,this.rebaseState=r}},Xt=class extends Error{static{a(this,"ProposalRebaseRaceError")}code="rebase_race";statusCode=409;constructor(t){super(`Proposal '${t}' could not be applied because the flows kept changing during approve. It is still pending \u2014 try again.`),this.name="ProposalRebaseRaceError"}},Pt=class extends Error{static{a(this,"ProposalDeployTimeoutError")}code="deploy_timeout";statusCode=504;constructor(){super("Deployment timed out; the deploy outcome is unknown. The proposal remains pending with deployPending set. Approvals wait until the runtime settles or the deploy hard cap is reached. Check the live flow before approving again."),this.name="ProposalDeployTimeoutError"}},Zt=class extends Error{static{a(this,"ProposalApproveQueueTimeoutError")}code="approve_queue_timeout";statusCode=504;constructor(){super("Approval timed out waiting in the queue. No deployment was started for this request. Check the live flow and retry."),this.name="ProposalApproveQueueTimeoutError"}},Lo=class extends Error{static{a(this,"DeployHardCapError")}},eh=3;a(vi,"deadlineMs");Ud=Promise.resolve(),No=class{constructor(t,n,r,o,s={}){this.proposals=t;this.audit=n;this.adapter=r;this.getDeploymentType=o;this.serviceOptions=s}static{a(this,"ProposalService")}async approve(t,n,r={}){let o=this.proposals.get(t),s=o.deployPending!==void 0;if(!U(o.status))throw new Y(t,o.status);if(!s&&this.proposals.isApproving(t))throw new Re(t);let i=Ud,d,l=new Promise(c=>{d=c});return Ud=i.then(()=>l),new Promise((c,u)=>{let p=!1,f,m=new Promise((g,y)=>{f=setTimeout(()=>y(new Zt),vi(this.serviceOptions.approveQueueTimeoutMs,6e4)),f.unref?.()});a(async()=>{try{await Promise.race([i,m]),clearTimeout(f);let g=this.proposals.get(t);if(s&&g.status==="approved")return g;let y=this.proposals.beginApprove(t);return p=!0,await this.approveLocked(y,n,r,u)}finally{clearTimeout(f),p&&this.proposals.releaseLock(t),d()}},"run")().then(c,u)})}async approveLocked(t,n,r,o){let s=t.id,i,d=jd(t),l=r.deploymentType??this.getDeploymentType(),c=t.type;if(c!=="replace_flow"){let f=this.proposals.markFailed(s,n,`Unsupported proposal type '${c}'`);return this.audit.append("proposal.failed",n,{...d,reason:f.reason}),f}if(To(t))throw this.proposals.releaseLock(s),te(this.proposals,{flowId:t.payload.flowId}),new Y(s,this.proposals.get(s).status);let u=$e(this.adapter),p=a((f,m)=>{let h=this.proposals.markApproved(s,n,m);return this.audit.append("proposal.approved",n,{...d,resultRev:m,liveRev:m,baseRev:f.expectedRev,alreadyApplied:!0}),h},"markAlreadyApplied");for(let f=0;f<eh;f++){let m=await u.getFlows(),h=this.proposals.get(s),g=zt(m.flows,h.payload.flowId,h.payload.flow),y=g&&br(g);if(y&&(y===h.deployPending?.mergedHash||y===br(h.payload.flow))){i=p(h,m.rev);break}let{result:w}=Me(this.proposals,h,m,{duringApprove:!0});if(w.state==="conflict"||!w.merged||!w.mergedHash)throw this.audit.append("proposal.conflict",n,{...d,liveRev:m.rev,conflictCount:w.conflicts.length}),new Je(s,li(w).conflicts);if(y===w.mergedHash){i=p(h,m.rev);break}if((w.state==="merged"||r.mergedHash!==void 0)&&r.mergedHash!==w.mergedHash)throw new Yt(s,w.mergedHash,w.state);let L=w.merged,P=w.mergedHash,I=!1;try{let D=await this.withDeployDeadline(()=>u.updateFlow({flowId:h.payload.flowId,flow:L,expectedRev:m.rev,deploymentType:l}),()=>{I=!0;let T=new Pt;this.proposals.recordDeployPending(s,{mergedHash:P,reason:"Deploy outcome unknown: the runtime has not settled. Approvals are waiting."}),this.audit.append("proposal.deploy_timeout",n,{...d,code:T.code}),o(T)},()=>{this.proposals.recordDeployPending(s,{mergedHash:P,queueReleased:!0,reason:"Deploy outcome unknown: the runtime did not settle before the hard cap. The approval queue was released. Check the live flow before approving again."}),o(new Pt)});i=this.proposals.markApproved(s,n,D.rev),this.audit.append("proposal.approved",n,{...d,resultRev:D.rev,flowId:h.payload.flowId,baseRev:h.expectedRev,liveRev:m.rev,rebaseState:w.state});break}catch(D){if(i||D instanceof Lo)throw D;if(I)throw this.proposals.recordDeployPending(s,void 0),D;if(D instanceof Q)continue;let T=D instanceof Error?D.message:String(D),x=this.proposals.markFailed(s,n,T);return this.audit.append("proposal.failed",n,{...d,reason:T}),x}}if(!i)throw new Xt(s);try{await vr(this.proposals,this.adapter,{flowId:i.payload.flowId})}catch{}return i}async withDeployDeadline(t,n,r){let o=setTimeout(n,vi(this.serviceOptions.deployTimeoutMs,6e4));o.unref?.();let s,i=new Promise((d,l)=>{s=setTimeout(()=>{try{r()}finally{l(new Lo)}},vi(this.serviceOptions.deployHardCapMs,3e5)),s.unref?.()});try{return await Promise.race([t(),i])}finally{clearTimeout(o),clearTimeout(s)}}reject(t,n,r){let o=this.proposals.get(t);if(!U(o.status))throw new Y(t,o.status);let s=jd(o),i=this.proposals.markRejected(t,n,r.trim()||"Rejected by admin");return this.audit.append("proposal.rejected",n,{...s,reason:i.reason}),i}}});function we(e,t,n={}){return ki(e,t,n)}function th(e,t){return t==="excess_ports"?e==="mcp"?"This node has more wire ports than its type declares \u2014 remove extra ports or raise the node outputs, then retry.":e==="rest"?"This node has more wire ports than its type declares \u2014 trim wires[] or raise outputs, then retry.":"This node has more wire ports than its type declares \u2014 trim wires or raise outputs, then retry.":t==="missing_target"?e==="mcp"?"Wire targets must be node ids in this flow \u2014 add the node in the same patch or use get_flow ids.":e==="rest"?"Wire targets must be node ids in this flow \u2014 add the node in the same patch or use GET /flows/{id} ids.":"wires must list existing node ids (or add those nodes in the same patch).":e==="mcp"||e==="rest"?"Fix wires: targets must exist in this flow, and port count must not exceed the node outputs.":"wires must list existing node ids (or add those nodes in the same patch)."}function nh(e){return e==="mcp"?"Call list_node_types or search_palette. Ask the user to install via Manage palette, then recheck. This API never installs.":e==="rest"?"Call GET /node-types?q= or GET /palette/search?q=. Ask the user to install via Manage palette, then recheck. This API never installs.":"Call GET \u2026/node-types?q= or \u2026/palette/search?q=. Ask the user to install via Manage palette, then recheck. This API never installs."}function _t(e,t,n,r){return e==="mcp"?t:e==="rest"?n:r}function ki(e,t,n={}){switch(e){case"version_mismatch":return _t(t,"Re-read with list_flows/get_flow and retry with the current rev.","Re-read the flow and retry with the current rev.","Re-read the flow and retry with the current rev.");case"node_not_found":return _t(t,"Call get_flow or get_node and use a node id from that response.","Call GET /flows/{id} and use a node id from that response.","Call GET \u2026/flows/<id> and use a node id from that response.");case"flow_not_found":return _t(t,"Call list_flows to list valid ids.","Call GET /flows to list valid ids.","Use an id from GET ./flows.");case"proposal_not_found":return _t(t,"Call list_proposals and use an id from that response.","List proposals and use an id from that response.","List proposals and use an id from that response.");case"proposal_not_owned":return"Use the creating access link to resolve or withdraw. If that token is lost, an admin with full permission can withdraw through POST /proposals/{id}/withdraw.";case"proposal_not_pending":return"Only open (pending or conflict) proposals can be withdrawn or rejected.";case"confirm_required":return"Add confirm=1 to the withdraw URL (required so link prefetchers cannot withdraw).";case"invalid_wires":return th(t,n.cause);case"node_type_not_found":case"unknown_node_type":return nh(t);case"missing_node_type":return'Each add op needs node.type as a non-empty string (e.g. "inject").';case"invalid_line_range":return _t(t,'Pass lines as "A" or "A-B" (1-based inclusive). Reversed ranges (end < start) are invalid.',"Use lines=A or lines=A-B (1-based inclusive). Reversed ranges (end < start) are invalid.","Use lines=A or lines=A-B (1-based inclusive). Reversed ranges (end < start) are invalid.");case"line_range_out_of_bounds":return"start is past the end of the field \u2014 check totalLines and request a lower lines range.";case"ambiguous_match":return"find matched more than once \u2014 pass occurrence=N (1-based) or a more unique find string.";case"missing_expected_rev":return _t(t,"Pass expectedRev from list_flows/get_flow (top-level, next to type and payload).","Pass expectedRev from GET /flows or GET /flows/{id} (top-level, next to type and payload).","Pass rev from GET \u2026/flows or \u2026/flows/<id>.");case"missing_query":return _t(t,"Pass a non-empty q string.","q query parameter is required.","q query parameter is required.");case"unsupported_media_type":return"Send Content-Type: application/json with a JSON body.";case"text_not_found":return"Re-read the node field; find must match exactly (case-sensitive).";case"field_not_found":return"Re-read the node; field must be an existing string property (template, func, \u2026).";case"field_not_string":return _t(t,"This tool only returns string fields (template, func, \u2026). Use get_node for other property types.","This endpoint only returns string fields (template, func, \u2026). Use GET /nodes/{id} for other property types.","This endpoint only returns string fields (template, func, \u2026). Use GET /nodes/{id} for other property types.");default:return}}function Tr(e,t,n={}){let r=n.canonicalizeMessage!==!1,o=null;if(e instanceof Je?o={statusCode:409,body:{error:e.message,code:e.code,conflicts:e.conflicts}}:e instanceof Yt?o={statusCode:409,body:{error:e.message,code:e.code,mergedHash:e.mergedHash}}:e instanceof Xt||e instanceof Pt||e instanceof Zt||e instanceof Re?o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}:e instanceof k?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,...e.details??{}}}:e instanceof Q?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,expectedRev:e.expectedRev,currentRev:e.currentRev}}:e instanceof X?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,flowId:e.flowId}}:e instanceof me||e instanceof wt||e instanceof Y||e instanceof ce?o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}:(e instanceof We||e instanceof Le)&&(o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}),!o)return null;let s=o.body.code;if(r&&!Array.isArray(o.body.errors)){let i=ot[s];i&&(o.body.error=i)}if(typeof o.body.hint!="string"||!o.body.hint){let i=ki(s,t,{cause:o.body.cause})??Pr(s);i&&(o.body.hint=i)}return o}function Bd(e,t,n={}){let o={error:n.error??ot[t]??"Request failed",code:t,...n.details??{}},s=ki(t,e,{cause:o.cause})??Pr(t);return s&&(o.hint=s),{statusCode:n.statusCode??400,body:o}}var ot,rt=q(()=>{"use strict";Te();Ve();oe();se();Io();Fo();ot={proposal_conflict:"Proposal conflicts with current editor changes",rebase_ack_required:"Review and acknowledge the merged proposal",rebase_race:"Flows kept changing during approval",deploy_timeout:"Deployment timed out; the deploy outcome is unknown. The proposal remains pending with deployPending set. Approvals wait until the runtime settles or the deploy hard cap is reached. Check the live flow before approving again.",approve_queue_timeout:"Approval timed out waiting in the queue. No deployment was started for this request. Check the live flow and retry.",conflict_not_found:"Conflict no longer exists",invalid_choice:"Invalid conflict choice",flow_id_mismatch:"Proposal flow id cannot change",node_not_found:"Node was not found",flow_not_found:"Flow was not found",field_not_found:"Field was not found",field_not_string:"Field is not a string",missing_query:"q query parameter is required",missing_flow_id:"Missing flow id",missing_node_id:"Missing node id",missing_field:"Missing field name",invalid_line_range:"lines must be A or A-B (1-based inclusive; end >= start)",line_range_out_of_bounds:"lines range is out of bounds",proposal_not_found:"Proposal was not found",proposal_not_owned:"Proposal was not created by this access link",proposal_not_pending:"Proposal is not pending",confirm_required:"Withdraw over GET requires confirm=1",unsupported_media_type:"Content-Type must be application/json",upstream_error:"Upstream request failed",internal_error:"Internal server error"};a(we,"channelErrorHint");a(th,"invalidWiresHint");a(nh,"nodeTypeHint");a(_t,"pickChannel");a(ki,"hintFor");a(Tr,"mapErrorForChannel");a(Bd,"buildChannelError")});var Uy={};Qi(Uy,{ACCESS_LINK_INVALID_MESSAGE:()=>$t,ACCESS_LINK_MISSING_MESSAGE:()=>Ie,AccessLinkStore:()=>an,EDITOR_SIDEBAR_ASSET_VERSION:()=>$n,MAX_PROPOSAL_JSON_BYTES:()=>K,SettingsStore:()=>hn,acceptedMediaTypes:()=>Xc,areAiFeaturesEnabled:()=>Ee,bindSettingsStore:()=>Vr,createPluginServices:()=>hs,default:()=>jy,getStorageDir:()=>ss,isAdminAuthEnabled:()=>re,isMcpStreamRequest:()=>Zc,packageVersion:()=>On,registerRoutes:()=>Qc,renderEditorSidebarJs:()=>Oo,renderEditorSidebarLoaderHtml:()=>Gd,requireAccessLink:()=>bn,resolveAiAccessSettings:()=>Ce});module.exports=iu(Uy);var Yc=require("fs");var jr=require("fs"),Xn=require("path");oe();function Ur(e){let t=(0,Xn.join)(e,".ai-access");return{root:t,proposals:(0,Xn.join)(t,"proposals"),logs:(0,Xn.join)(t,"logs"),settings:(0,Xn.join)(t,"config.json")}}a(Ur,"resolveAiAccessPaths");function ws(e){(0,jr.mkdirSync)(e,{recursive:!0,mode:ys});try{(0,jr.chmodSync)(e,ys)}catch{}}a(ws,"mkdirSecure");function oa(e){ws(e.root),ws(e.proposals),ws(e.logs)}a(oa,"ensureAiAccessDirs");Mt();ze();function Qr(e){let t=Ae(e);return`auth-tokens${(t==="/"?"":t).replace(/\//g,"-")}`}a(Qr,"editorAuthTokenKey");ze();rt();function rh(e){if(!e)return!1;let t=e.split(";")[0]?.trim().toLowerCase()??"";return t==="application/json"||t.endsWith("+json")}a(rh,"isJsonContentType");function oh(e){let t=e.headers["content-length"];if(typeof t=="string"&&t.length>0){let n=Number(t);if(Number.isFinite(n)&&n>0)return!0;if(n===0)return!1}return e.headers["transfer-encoding"]!==void 0}a(oh,"hasRequestBody");function je(e="rest"){return(t,n,r)=>{let o=t.headers["content-type"];if(rh(o)){r();return}if(!o&&!oh(t)){r();return}let s=Bd(e,"unsupported_media_type",{statusCode:415});n.status(s.statusCode).json(s.body)}}a(je,"requireJsonContentType");var sh="text/plain; charset=utf-8",ih="text/html; charset=utf-8",ah="default-src 'none'; base-uri 'self'; form-action 'none'; frame-ancestors 'none'";function dh(e){let t=e.query?.format;return typeof t=="string"?t.toLowerCase():void 0}a(dh,"formatParam");function lh(e){let t=dh(e);if(t==="md"||t==="json")return"native";if(!e.get("accept"))return"html";let n=e.accepts(["text/html","application/json","text/markdown","text/plain"]);return n==="application/json"||n==="text/markdown"?"native":n==="text/plain"?"plain":"html"}a(lh,"chooseGetLinkRepresentation");function qd(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}a(qd,"escapeHtml");var ch=/(?<![A-Za-z0-9_.~\-/])\.\/[A-Za-z0-9._~\-/]+(?:\?[A-Za-z0-9._~\-=&%]+)?/g;function uh(e){return e.replace(ch,(t,n)=>{if(e.startsWith("&lt;",n+t.length))return t;let r=t.replace(/[.]+$/,""),o=t.slice(r.length);return r.length<=2||r.includes("..")||r.includes("//")||r.includes("&")?t:`<a href="${r}">${r}</a>${o}`})}a(uh,"linkifyEscaped");function ph(e,t){return`<!doctype html>
11
+ <html><head><meta charset="utf-8">${t?`<base href="${qd(t)}">`:""}<title>Node-RED AI Access</title></head><body><pre>${uh(qd(e))}</pre></body></html>
12
+ `}a(ph,"renderGetLinkHtml");function fh(e){let t=e.split("?")[0]??"",n=/^(.*?\/mcp\/a\/[A-Za-z0-9_-]+)(?:\/|$)/.exec(t);if(!(!n?.[1]||!/^[A-Za-z0-9._~\-/]*$/.test(n[1])))return`${n[1]}/`}a(fh,"getLinkBaseHref");function hh(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}a(hh,"prettyJson");function zd(){return(e,t,n)=>{let r=lh(e),o=fh(e.originalUrl);t.vary("Accept"),t.setHeader("Cache-Control","no-store"),t.setHeader("Referrer-Policy","no-referrer"),t.setHeader("X-Robots-Tag","noindex, nofollow"),t.setHeader("X-Content-Type-Options","nosniff");let s=t.send.bind(t);t.send=(i=>{let d=String(t.getHeader("Content-Type")??"").toLowerCase(),l=d.startsWith("application/json"),c=d.startsWith("text/markdown");if(r==="native"||!l&&!c||t.headersSent)return s(i);let u=typeof i=="string"?i:Buffer.isBuffer(i)?i.toString("utf8"):void 0;return u===void 0?s(i):r==="plain"?(t.setHeader("Content-Type",sh),s(u)):(t.setHeader("Content-Type",ih),t.setHeader("Content-Security-Policy",ah),s(ph(l?hh(u):u,o)))}),n()}}a(zd,"getLinkContentNegotiation");Br();ze();function mh(e){let t=xe(e.settings.httpAdminRoot),n=mt(e.settings.httpAdminRoot),r="0.2.0-beta.8",o=[{method:"GET",path:"/",auth:"none",summary:"This discovery index"},{method:"GET",path:"/openapi.yaml",auth:"none",summary:"OpenAPI 3.1 specification (YAML)"},{method:"GET",path:"/openapi.json",auth:"none",summary:"OpenAPI 3.1 specification (JSON)"},{method:"GET",path:"/status",auth:"admin-or-access-link",summary:"Plugin status and caller info"},{method:"GET",path:"/flows",auth:"admin-or-access-link",summary:"List flows + rev"},{method:"GET",path:"/flows/{id}",auth:"admin-or-access-link",summary:"Get one flow tab + rev (?summary=1 for node list without field contents)"},{method:"GET",path:"/nodes/{id}",auth:"admin-or-access-link",summary:"Get one full node (optional ?flow=)"},{method:"GET",path:"/nodes/{id}/field/{name}",auth:"admin-or-access-link",summary:"Get one node field (?lines=A-B optional)"},{method:"GET",path:"/search",auth:"admin-or-access-link",summary:"Text search across string fields (?q=&flow=)"},{method:"GET",path:"/node-types",auth:"admin-or-access-link",summary:"List installed palette node types (?q= filter)"},{method:"GET",path:"/node-types/{type}",auth:"admin-or-access-link",summary:"Detail for one installed node type (defaults JSON, inputs/outputs, help; ?help=0 omits help)"},{method:"GET",path:"/palette/search",auth:"admin-or-access-link",summary:"Search Node-RED catalogue (recommend only; never installs) (?q=)"},{method:"POST",path:"/proposals",auth:"access-link",summary:"Create a replace_flow proposal"},{method:"GET",path:"/proposals",auth:"admin-or-access-link",summary:"List proposals (admin: all; link: own)"},{method:"GET",path:"/proposals/{id}",auth:"admin-or-access-link",summary:"Get one proposal"},{method:"GET",path:"/proposals/{id}/diff",auth:"admin-or-access-link",summary:"Diff proposal vs current flow"}];return{ok:!0,name:"node-red-ai-access",version:r,description:"Temporary, owner-approved Node-RED access for AI clients over plain HTTP (REST) or MCP.",auth:{scheme:"Bearer",header:"Authorization: Bearer <token>",description:"An admin with * mints tokens on the /ai-access dashboard (Access tokens tab) (POST /links only \u2014 never under GET /mcp/a/<token>). Each token has kind get_link | rest | mcp and only works on that surface. Send rest/mcp tokens as Authorization: Bearer <token>. Missing/invalid \u2192 401 access_link_missing / access_link_invalid; wrong kind \u2192 403 wrong_token_kind. GET /mcp/a links cannot create tokens.",tokenKinds:["get_link","rest","mcp"],mint:{path:"/links",method:"POST",auth:"admin",note:"Admin * only. Not available under /mcp/a/<token>."}},openapi:`${t}/openapi.yaml`,openapiJson:`${t}/openapi.json`,mcp:n,apiBase:t,getLink:{base:"/mcp/a/<token>",canMintTokens:!1,note:"Self-contained GET surface for get_link tokens only. No POST /links equivalent."},endpoints:o}}a(mh,"buildDiscoveryIndex");function Ai(e){return(t,n)=>{n.status(200).json(mh(e))}}a(Ai,"discoveryIndexHandler");function Wd(){return(e,t)=>{t.status(404).json({error:"Not found",code:"not_found"})}}a(Wd,"mcpApiNotFoundHandler");var Fn;function On(){return Fn||(Fn="0.2.0-beta.8",Fn)}a(On,"packageVersion");var $n=On();function gh(){return`#red-ui-sidebar-ai-access {
13
13
  position: relative;
14
14
  height: 100%;
15
15
  display: flex;
@@ -165,7 +165,7 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
165
165
  border: 0;
166
166
  background: transparent;
167
167
  color: transparent;
168
- }`}a(uh,"editorSidebarStyles");function ph(){return`
168
+ }`}a(gh,"editorSidebarStyles");function yh(){return`
169
169
  /**
170
170
  * Editor right-hand sidebar tab: AI Access.
171
171
  * Served from /mcp/editor/ai-access-sidebar.js (no-store + ?v= cache-bust).
@@ -463,7 +463,7 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
463
463
  saving = false;
464
464
  });
465
465
  }
466
- `}a(ph,"editorSidebarScriptPartA");function fh(){return`
466
+ `}a(yh,"editorSidebarScriptPartA");function wh(){return`
467
467
  function buildContent() {
468
468
  var content = $('<div></div>', { id: CONTENT_ID, class: 'red-ui-sidebar-ai-access' });
469
469
 
@@ -565,7 +565,7 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
565
565
  },
566
566
  });
567
567
  })();
568
- `}a(fh,"editorSidebarScriptPartB");function hh(){return ph()+fh()}a(hh,"editorSidebarScriptBody");function mh(){return`(function () {
568
+ `}a(wh,"editorSidebarScriptPartB");function bh(){return yh()+wh()}a(bh,"editorSidebarScriptBody");function vh(){return`(function () {
569
569
  if (typeof RED === 'undefined' || typeof BroadcastChannel === 'undefined') return;
570
570
  if (window.__com55AiAccessRevealListener) return;
571
571
  window.__com55AiAccessRevealListener = true;
@@ -596,8 +596,8 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
596
596
  try { window.focus(); } catch (e) {}
597
597
  }
598
598
  };
599
- })();`}a(mh,"editorRevealListenerScript");function Lo(){let e=JSON.stringify(uh());return[`/* @com55/node-red-ai-access editor sidebar v=${Cn} */`,"(function(){"," if (typeof document === 'undefined') return;"," if (!document.getElementById('com55-ai-access-sidebar-css')) {"," var style = document.createElement('style');"," style.id = 'com55-ai-access-sidebar-css';",` style.textContent = ${e};`," document.head.appendChild(style);"," }","})();",mh(),hh()].join(`
600
- `)}a(Lo,"renderEditorSidebarJs");function Ud(e=Cn){return`<script>
599
+ })();`}a(vh,"editorRevealListenerScript");function Oo(){let e=JSON.stringify(gh());return[`/* @com55/node-red-ai-access editor sidebar v=${$n} */`,"(function(){"," if (typeof document === 'undefined') return;"," if (!document.getElementById('com55-ai-access-sidebar-css')) {"," var style = document.createElement('style');"," style.id = 'com55-ai-access-sidebar-css';",` style.textContent = ${e};`," document.head.appendChild(style);"," }","})();",vh(),bh()].join(`
600
+ `)}a(Oo,"renderEditorSidebarJs");function Gd(e=$n){return`<script>
601
601
  /**
602
602
  * Thin loader for the AI Access editor sidebar.
603
603
  * Logic lives at GET /mcp/editor/ai-access-sidebar.js (Cache-Control: no-store)
@@ -631,8 +631,8 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
631
631
  (document.head || document.documentElement).appendChild(s);
632
632
  })();
633
633
  </script>
634
- `}a(Ud,"renderEditorSidebarLoaderHtml");ie();tt();Pe();function Bd(e){return nt[e]??"Request failed"}a(Bd,"safeErrorText");function gh(e,t){let n=W(t,"rest");if(!n)return!1;let r=n.body.code,o={error:Bd(r),code:r};return typeof n.body.hint=="string"&&(o.hint=n.body.hint),typeof n.body.expectedRev=="string"&&(o.expectedRev=n.body.expectedRev),typeof n.body.currentRev=="string"&&(o.currentRev=n.body.currentRev),e.setHeader("X-Content-Type-Options","nosniff"),e.status(n.statusCode).json(o),!0}a(gh,"sendMappedFlowError");function qd(e){return async(t,n)=>{try{n.json(await ut(Xe(t,e)))}catch(r){if(gh(n,r))return;n.setHeader("X-Content-Type-Options","nosniff"),n.status(502).json({error:Bd("upstream_error"),code:"upstream_error"})}}}a(qd,"listFlowsHandler");ve();ie();Be();tt();function In(e){return e==null||e===""?{ok:!0,format:"summary"}:e==="summary"||e==="full"?{ok:!0,format:e}:{ok:!1}}a(In,"resolveDiffFormat");function yh(e,t=2){if(e.length===0)return{lines:e,truncated:!1};let n=new Array(e.length).fill(!1);for(let i=0;i<e.length;i+=1){let d=e[i];if(!d||d.op==="equal")continue;let l=Math.max(0,i-t),c=Math.min(e.length-1,i+t);for(let u=l;u<=c;u+=1)n[u]=!0}if(!n.some(Boolean)){let i=Math.min(e.length,t*2+1);for(let d=0;d<i;d+=1)n[d]=!0}let r=[],o=!1,s=0;for(;s<e.length;){if(n[s]){let l=e[s];l&&r.push(l),s+=1;continue}let i=s;for(;i<e.length&&!n[i];)i+=1;let d=i-s;o=!0,r.push({op:"equal",text:`\u2026 (${d} unchanged line${d===1?"":"s"}) \u2026`}),s=i}return{lines:r,truncated:o}}a(yh,"compactLineDiff");function wh(e,t){let n=e.before??"",r=e.after??"",o=Buffer.byteLength(n,"utf8"),s=Buffer.byteLength(r,"utf8");if(t==="full")return{...e,before:n,after:r,beforeBytes:o,afterBytes:s};let i=yh(e.lines),d={key:e.key,kind:"text",beforeBytes:o,afterBytes:s,lines:i.lines};return i.truncated&&(d.truncated=!0),d}a(wh,"formatTextChange");function bh(e,t){return e.map(n=>n.kind==="text"?wh(n,t):n)}a(bh,"formatPropertyChanges");function zd(e,t){if(t==="full")return e;let n=a(r=>{if(typeof r!="string")return r;let o=Buffer.byteLength(r,"utf8");return o<=4e3?r:`${r.slice(0,500)}
635
- \u2026 (${o} bytes total; use ?format=full for complete text) \u2026`},"trimText");return{...e,keyFields:e.keyFields.map(r=>r.kind==="text"?{...r,value:n(r.value)}:r),properties:e.properties.map(r=>r.kind==="text"?{...r,value:n(r.value)}:r)}}a(zd,"formatPresenceText");function gi(e,t){return{summary:e.summary,added:e.added.map(n=>zd(n,t)),removed:e.removed.map(n=>zd(n,t)),modified:e.modified.map(n=>({...n,changes:bh(n.changes,t)}))}}a(gi,"formatSections");function Ln(e,t="summary"){let n=gi(e,t),r={...e,...n,format:t};return e.liveChanges&&(r.liveChanges=gi(e.liveChanges,t)),e.vsCurrent&&(r.vsCurrent=gi(e.vsCurrent,t)),r}a(Ln,"applyDiffFormat");se();ve();_o();function Nn(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}a(Nn,"escapeForDocs");var Wd="https://YOUR-NODE-RED/mcp/rpc";function vh(e,t,n,r=Wd){return`## Also available (prefer when your client can send headers)
634
+ `}a(Gd,"renderEditorSidebarLoaderHtml");se();rt();Pe();function Kd(e){return ot[e]??"Request failed"}a(Kd,"safeErrorText");function kh(e,t){let n=W(t,"rest");if(!n)return!1;let r=n.body.code,o={error:Kd(r),code:r};return typeof n.body.hint=="string"&&(o.hint=n.body.hint),typeof n.body.expectedRev=="string"&&(o.expectedRev=n.body.expectedRev),typeof n.body.currentRev=="string"&&(o.currentRev=n.body.currentRev),e.setHeader("X-Content-Type-Options","nosniff"),e.status(n.statusCode).json(o),!0}a(kh,"sendMappedFlowError");function Jd(e){return async(t,n)=>{try{n.json(await pt(Xe(t,e)))}catch(r){if(kh(n,r))return;n.setHeader("X-Content-Type-Options","nosniff"),n.status(502).json({error:Kd("upstream_error"),code:"upstream_error"})}}}a(Jd,"listFlowsHandler");ve();se();ze();rt();function Mn(e){return e==null||e===""?{ok:!0,format:"summary"}:e==="summary"||e==="full"?{ok:!0,format:e}:{ok:!1}}a(Mn,"resolveDiffFormat");function Ah(e,t=2){if(e.length===0)return{lines:e,truncated:!1};let n=new Array(e.length).fill(!1);for(let i=0;i<e.length;i+=1){let d=e[i];if(!d||d.op==="equal")continue;let l=Math.max(0,i-t),c=Math.min(e.length-1,i+t);for(let u=l;u<=c;u+=1)n[u]=!0}if(!n.some(Boolean)){let i=Math.min(e.length,t*2+1);for(let d=0;d<i;d+=1)n[d]=!0}let r=[],o=!1,s=0;for(;s<e.length;){if(n[s]){let l=e[s];l&&r.push(l),s+=1;continue}let i=s;for(;i<e.length&&!n[i];)i+=1;let d=i-s;o=!0,r.push({op:"equal",text:`\u2026 (${d} unchanged line${d===1?"":"s"}) \u2026`}),s=i}return{lines:r,truncated:o}}a(Ah,"compactLineDiff");function xh(e,t){let n=e.before??"",r=e.after??"",o=Buffer.byteLength(n,"utf8"),s=Buffer.byteLength(r,"utf8");if(t==="full")return{...e,before:n,after:r,beforeBytes:o,afterBytes:s};let i=Ah(e.lines),d={key:e.key,kind:"text",beforeBytes:o,afterBytes:s,lines:i.lines};return i.truncated&&(d.truncated=!0),d}a(xh,"formatTextChange");function Sh(e,t){return e.map(n=>n.kind==="text"?xh(n,t):n)}a(Sh,"formatPropertyChanges");function Vd(e,t){if(t==="full")return e;let n=a(r=>{if(typeof r!="string")return r;let o=Buffer.byteLength(r,"utf8");return o<=4e3?r:`${r.slice(0,500)}
635
+ \u2026 (${o} bytes total; use ?format=full for complete text) \u2026`},"trimText");return{...e,keyFields:e.keyFields.map(r=>r.kind==="text"?{...r,value:n(r.value)}:r),properties:e.properties.map(r=>r.kind==="text"?{...r,value:n(r.value)}:r)}}a(Vd,"formatPresenceText");function xi(e,t){return{summary:e.summary,...e.tabProperties?{tabProperties:e.tabProperties}:{},added:e.added.map(n=>Vd(n,t)),removed:e.removed.map(n=>Vd(n,t)),modified:e.modified.map(n=>({...n,changes:Sh(n.changes,t)}))}}a(xi,"formatSections");function Dn(e,t="summary"){let n=xi(e,t),r={...e,...n,format:t};return e.liveChanges&&(r.liveChanges=xi(e.liveChanges,t)),e.vsCurrent&&(r.vsCurrent=xi(e.vsCurrent,t)),r}a(Dn,"applyDiffFormat");oe();ve();Io();function Hn(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}a(Hn,"escapeForDocs");var Yd="https://YOUR-NODE-RED/mcp/rpc";function Th(e,t,n,r=Yd){return`## Also available (prefer when your client can send headers)
636
636
 
637
637
  **Prefer MCP or REST** whenever the AI client can send custom headers and JSON
638
638
  bodies \u2014 no URL length limits and no chunked drafts. Use this **GET link
@@ -701,7 +701,7 @@ curl -sS -X POST "${r}" \\
701
701
  -H "Accept: application/json, text/event-stream" \\
702
702
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
703
703
  \`\`\`
704
- `}a(vh,"docsAlsoAvailable");function kh(e,t){return`# Node-RED AI Access \u2014 self-contained GET link (get_link)${e}
704
+ `}a(Th,"docsAlsoAvailable");function Rh(e,t){return`# Node-RED AI Access \u2014 self-contained GET link (get_link)${e}
705
705
 
706
706
  **Git mental model:** a proposal is like a pull request. Human approve = merge;
707
707
  reject = close; withdraw = you close your own PR. Revision changes auto-rebase
@@ -779,7 +779,7 @@ fields at the top level** (no \`ok\` / \`proposal\` wrapper). Read
779
779
 
780
780
  Lean by default (no \`payload\` / \`baseFlow\`). Pass
781
781
  \`?include=payload,baseFlow\` or \`?include_payload=true\` to include them.
782
- `}a(kh,"docsIntro");function Ah(){return"## Step-by-step workflow\n\n1. `GET ./status` \u2014 confirm the link works; note `caller.expiresAt` /\n `caller.minutesRemaining`.\n2. `GET ./flows` \u2014 list tabs + current `rev`.\n3. **Layered read (prefer this before loading whole tabs):**\n - `GET ./flows/<id>?summary=1` \u2014 node list with `id`/`type`/`name`/`wires` +\n per-node `bytes` (no field contents).\n - `GET ./search?q=<text>&flow=<id>` \u2014 hits with node id, field, line numbers,\n short snippet (`truncated:true` when the result cap is hit).\n - `GET ./nodes/<id>` \u2014 one full node (`flowId` included when known).\n - `GET ./nodes/<id>/field/<name>?lines=A-B` \u2014 one field (optional 1-based\n line range) plus `totalLines` and `bytes`.\n4. Prefer a **targeted text replace** for small edits inside long string fields\n (`template`, `func`, \u2026) \u2014 unique `find` (or `occurrence`) keeps the diff short:\n\n```\nGET ./propose?flow=<flowId>&rev=<rev>&node=<nodeId>&field=<prop>&find=<text>&replace=<text>&occurrence=<n>&title=<text>\n```\n\n5. Or set whole properties (optional `title` \u226480 chars; `summary` is a title fallback):\n\n```\nGET ./propose?flow=<flowId>&rev=<rev>&node=<nodeId>&set.<prop>=<urlencoded>&title=<text>\n```\n\n6. Or multi-node / add / remove / `replace_text` / full tab via base64url JSON patch:\n\n```\nGET ./propose?flow=<flowId>&rev=<rev>&patch=<base64url-json>&title=<text>\n```\n\n7. Poll `GET ./proposals` / `./proposals/<id>` until approved, rejected, or\n withdrawn. Every token can list/read **all** proposals; each item has\n `mine:true|false`. Filter with `?mine=true` (own) or `?mine=false` (others'),\n `?status=pending,superseded` (comma-separated or repeated), `?flowId=`;\n paginate with `?limit=` (1\u2013200) / `?offset=` (response includes `total`,\n `offset`, `hasMore`, and `nextOffset` when more remain). Invalid filters\n return `400 invalid_filter` naming the parameter. Cite `displayName` +\n `reviewUrl` when updating the user.\n8. Optional: `GET ./proposals/<id>/diff` (default `format=summary` compact\n line hunks + byte sizes; `format=full` for complete text) against the\n **base snapshot** stored at submit. Added/removed nodes are presence blocks\n with a summary sentence; open proposals include `rebase` and `liveChanges`; `vsCurrent` shows the merged deployment when there are no conflicts.\n9. Withdraw your own open (pending or conflict) proposal:\n `GET ./proposals/<id>/withdraw?confirm=1&reason=<text>` (status becomes `withdrawn`,\n not `rejected`). Only the link that created a proposal can withdraw it;\n others get `403 proposal_not_owned`.\n\n**Recommended edit loop:** summary \u2192 search \u2192 read a line range \u2192\n`replace_text` \u2192 propose.\n\nOn submit and on read of an open (pending or conflict) proposal, the response may include\n`conflictingProposals` (overlapping open work; list omits this) with\n`overlappingNodeIds` / `overlappingFields` when detectable \u2014 treat like a\ngit conflict warning; prefer one pending change per flow when possible.\n\n## Node types and palette (read-only)\n\nBefore proposing unfamiliar nodes:\n\n1. `GET ./node-types?q=<text>` \u2014 list installed palette types (filter optional;\n results capped; may include `truncated` / `total`).\n2. `GET ./node-types/<type>` \u2014 detail (`defaults` as JSON, `inputs`/`outputs`,\n `requiredConfigNodeTypes`, help sections). Pass `?help=0` to omit help.\n Types are **case-sensitive** (`Debug` \u2192 did you mean `debug`?).\n3. `GET ./palette/search?q=<text>` \u2014 search the public Node-RED catalogue.\n **Recommend only** \u2014 ask the user to install via **Manage palette**, then\n recheck `./node-types`. This API **never installs** packages.\n Core `node-red` types are **not** in the catalogue \u2014 use `./node-types`.\n\nSubmit/propose with an unknown `node.type` returns `400 unknown_node_type`\nwith `nearest` suggestions \u2014 call node-types / palette search and ask the user\nto install the module, then retry.\n"}a(Ah,"docsWorkflow");function Sh(){return'## Exact URLs for this token (relative)\n\n| Purpose | Method + path |\n| --- | --- |\n| These docs | `GET ./` (this URL) |\n| Status | `GET ./status` |\n| List flows | `GET ./flows` |\n| One flow | `GET ./flows/<id>` |\n| Flow summary (no field bodies) | `GET ./flows/<id>?summary=1` |\n| One node | `GET ./nodes/<id>` |\n| One field (optional lines) | `GET ./nodes/<id>/field/<name>?lines=A-B` |\n| Text search | `GET ./search?q=&flow=` |\n| List node types | `GET ./node-types?q=` |\n| One node type | `GET ./node-types/<type>` (`?help=0` omits help) |\n| Palette search (recommend only) | `GET ./palette/search?q=` |\n| All proposals (`?mine=` / `?status=` / `?flowId=` / `?limit=` / `?offset=`) | `GET ./proposals` |\n| One proposal | `GET ./proposals/<id>` |\n| Proposal diff | `GET ./proposals/<id>/diff` |\n| Withdraw own pending | `GET ./proposals/<id>/withdraw?confirm=1&reason=` |\n| Propose (query) | `GET ./propose?\u2026` |\n| Draft start | `GET ./draft/new?flow=&rev=&node=&field=&title=` |\n| Draft chunk | `GET ./draft/<draftId>/append?seq=N&data=<base64url>` |\n| Draft submit | `GET ./draft/<draftId>/submit?sha256=<hex>&title=` |\n\nThere is **no** `POST ./links` (or any create-link) under this URL \u2014 GET links\n**cannot mint tokens**.\n\nUnknown paths under this link return JSON `{ "code": "not_found" }` (never HTML,\nand the token is never echoed). Attempted mint paths (`./links`) return\n`403 link_create_not_supported`.\n'}a(Sh,"docsUrlTable");function xh(){return`## Propose examples
782
+ `}a(Rh,"docsIntro");function Ph(){return"## Step-by-step workflow\n\n1. `GET ./status` \u2014 confirm the link works; note `caller.expiresAt` /\n `caller.minutesRemaining`.\n2. `GET ./flows` \u2014 list tabs + current `rev`.\n3. **Layered read (prefer this before loading whole tabs):**\n - `GET ./flows/<id>?summary=1` \u2014 node list with `id`/`type`/`name`/`wires` +\n per-node `bytes` (no field contents).\n - `GET ./search?q=<text>&flow=<id>` \u2014 hits with node id, field, line numbers,\n short snippet (`truncated:true` when the result cap is hit).\n - `GET ./nodes/<id>` \u2014 one full node (`flowId` included when known).\n - `GET ./nodes/<id>/field/<name>?lines=A-B` \u2014 one field (optional 1-based\n line range) plus `totalLines` and `bytes`.\n4. Prefer a **targeted text replace** for small edits inside long string fields\n (`template`, `func`, \u2026) \u2014 unique `find` (or `occurrence`) keeps the diff short:\n\n```\nGET ./propose?flow=<flowId>&rev=<rev>&node=<nodeId>&field=<prop>&find=<text>&replace=<text>&occurrence=<n>&title=<text>\n```\n\n5. Or set whole properties (optional `title` \u226480 chars; `summary` is a title fallback):\n\n```\nGET ./propose?flow=<flowId>&rev=<rev>&node=<nodeId>&set.<prop>=<urlencoded>&title=<text>\n```\n\n6. Or multi-node / add / remove / `replace_text` / full tab via base64url JSON patch:\n\n```\nGET ./propose?flow=<flowId>&rev=<rev>&patch=<base64url-json>&title=<text>\n```\n\n7. Poll `GET ./proposals` / `./proposals/<id>` until approved, rejected, or\n withdrawn. Every token can list/read **all** proposals; each item has\n `mine:true|false`. Filter with `?mine=true` (own) or `?mine=false` (others'),\n `?status=pending,superseded` (comma-separated or repeated), `?flowId=`;\n paginate with `?limit=` (1\u2013200) / `?offset=` (response includes `total`,\n `offset`, `hasMore`, and `nextOffset` when more remain). Invalid filters\n return `400 invalid_filter` naming the parameter. Cite `displayName` +\n `reviewUrl` when updating the user.\n8. Optional: `GET ./proposals/<id>/diff` (default `format=summary` compact\n line hunks + byte sizes; `format=full` for complete text) against the\n **base snapshot** stored at submit. Added/removed nodes are presence blocks\n with a summary sentence; open proposals include `rebase` and `liveChanges`; `vsCurrent` shows the merged deployment when there are no conflicts.\n9. Withdraw your own open (pending or conflict) proposal:\n `GET ./proposals/<id>/withdraw?confirm=1&reason=<text>` (status becomes `withdrawn`,\n not `rejected`). Only the link that created a proposal can withdraw it;\n others get `403 proposal_not_owned`. If the token is lost, an admin with full permission can withdraw using an authenticated dashboard session via `POST /mcp/api/v1/proposals/<id>/withdraw`; this recovery is audit-logged.\n\n**Recommended edit loop:** summary \u2192 search \u2192 read a line range \u2192\n`replace_text` \u2192 propose.\n\nOn submit and on read of an open (pending or conflict) proposal, the response may include\n`conflictingProposals` (overlapping open work; list omits this) with\n`overlappingNodeIds` / `overlappingFields` when detectable \u2014 treat like a\ngit conflict warning; prefer one pending change per flow when possible.\n\n## Node types and palette (read-only)\n\nBefore proposing unfamiliar nodes:\n\n1. `GET ./node-types?q=<text>` \u2014 list installed palette types (filter optional;\n results capped; may include `truncated` / `total`).\n2. `GET ./node-types/<type>` \u2014 detail (`defaults` as JSON, `inputs`/`outputs`,\n `requiredConfigNodeTypes`, help sections). Pass `?help=0` to omit help.\n Types are **case-sensitive** (`Debug` \u2192 did you mean `debug`?).\n3. `GET ./palette/search?q=<text>` \u2014 search the public Node-RED catalogue.\n **Recommend only** \u2014 ask the user to install via **Manage palette**, then\n recheck `./node-types`. This API **never installs** packages.\n Core `node-red` types are **not** in the catalogue \u2014 use `./node-types`.\n\nSubmit/propose with an unknown `node.type` returns `400 unknown_node_type`\nwith `nearest` suggestions \u2014 call node-types / palette search and ask the user\nto install the module, then retry.\n"}a(Ph,"docsWorkflow");function _h(){return'## Exact URLs for this token (relative)\n\n| Purpose | Method + path |\n| --- | --- |\n| These docs | `GET ./` (this URL) |\n| Status | `GET ./status` |\n| List flows | `GET ./flows` |\n| One flow | `GET ./flows/<id>` |\n| Flow summary (no field bodies) | `GET ./flows/<id>?summary=1` |\n| One node | `GET ./nodes/<id>` |\n| One field (optional lines) | `GET ./nodes/<id>/field/<name>?lines=A-B` |\n| Text search | `GET ./search?q=&flow=` |\n| List node types | `GET ./node-types?q=` |\n| One node type | `GET ./node-types/<type>` (`?help=0` omits help) |\n| Palette search (recommend only) | `GET ./palette/search?q=` |\n| All proposals (`?mine=` / `?status=` / `?flowId=` / `?limit=` / `?offset=`) | `GET ./proposals` |\n| One proposal | `GET ./proposals/<id>` |\n| Proposal diff | `GET ./proposals/<id>/diff` |\n| Withdraw own pending | `GET ./proposals/<id>/withdraw?confirm=1&reason=` |\n| Propose (query) | `GET ./propose?\u2026` |\n| Draft start | `GET ./draft/new?flow=&rev=&node=&field=&title=` |\n| Draft chunk | `GET ./draft/<draftId>/append?seq=N&data=<base64url>` |\n| Draft submit | `GET ./draft/<draftId>/submit?sha256=<hex>&title=` |\n\nThere is **no** `POST ./links` (or any create-link) under this URL \u2014 GET links\n**cannot mint tokens**.\n\nUnknown paths under this link return JSON `{ "code": "not_found" }` (never HTML,\nand the token is never echoed). Attempted mint paths (`./links`) return\n`403 link_create_not_supported`.\n'}a(_h,"docsUrlTable");function Ch(){return`## Propose examples
783
783
 
784
784
  **Recommended for small edits to long fields** \u2014 \`replace_text\` (find must be
785
785
  **unique** unless you pass \`occurrence\` as a 1-based index). Creates a normal
@@ -857,9 +857,9 @@ GET ./propose?flow=tab1&rev=abc123&patch=<base64url>
857
857
  Node-level proposes create a normal \`replace_flow\` proposal; owners should
858
858
  approve with deployment type **\`nodes\`** (plugin default) so only changed
859
859
  nodes deploy.
860
- `}a(xh,"docsProposeExamples");function Th(){return`## Chunked drafts (long HTML / large fields)
860
+ `}a(Ch,"docsProposeExamples");function Eh(){return`## Chunked drafts (long HTML / large fields)
861
861
 
862
- When the GET URL would exceed **${gn} bytes**, do not
862
+ When the GET URL would exceed **${wn} bytes**, do not
863
863
  stuff everything into \`propose\`. Use drafts (expire after **30 minutes**,
864
864
  max **${K}** decoded bytes):
865
865
 
@@ -906,7 +906,7 @@ Or a full \`replace_flow\` document. JSON-mode drafts **may add nodes**, so
906
906
  \`draft/new\` does not require an existing \`node\`/\`field\`. Use JSON mode for
907
907
  structural changes; use field-mode only when you need to replace one whole
908
908
  string field that is too large for a single propose URL.
909
- `}a(Th,"docsDrafts");function Rh(){return`## Last resort: content larger than chunked drafts
909
+ `}a(Eh,"docsDrafts");function Ih(){return`## Last resort: content larger than chunked drafts
910
910
 
911
911
  If the field is still too large for chunked drafts (decoded size over the
912
912
  proposal cap), use this fallback only after drafts fail:
@@ -920,7 +920,7 @@ proposal cap), use this fallback only after drafts fail:
920
920
  **Trade-off:** pasted content is **not** reviewed through the proposal diff.
921
921
  Prefer \`replace_text\` or chunked drafts whenever they fit so the human can
922
922
  still see a readable change on \`/mcp\`.
923
- `}a(Rh,"docsLastResortPaste");function Ph(){return`## Size cap and REST/MCP nodes payload shapes
923
+ `}a(Ih,"docsLastResortPaste");function Lh(){return`## Size cap and REST/MCP nodes payload shapes
924
924
 
925
925
  The proposal JSON cap is **${K} bytes**
926
926
  (\`MAX_PROPOSAL_JSON_BYTES\` / ~1.5MB) on **every** channel: GET propose/drafts,
@@ -966,7 +966,7 @@ Example (canonical):
966
966
  **Alternative (also accepted):** top-level \`nodes\` array as an alias for
967
967
  \`payload.nodes\` (same op shapes). Full-tab replace remains supported via
968
968
  \`payload.flow\` (from \`GET ./flows/<id>\` / REST \`GET /flows/{id}\`).
969
- `}a(Ph,"docsRestNodesPayload");function _h(){return`## Known issues
969
+ `}a(Lh,"docsRestNodesPayload");function Nh(){return`## Known issues
970
970
 
971
971
  - **Rate limits are per token (and per IP).** Each access-link token has its own
972
972
  token bucket. Several tokens can each use the full per-token limit at the same
@@ -974,20 +974,20 @@ Example (canonical):
974
974
  - **\`RateLimit-*\` headers describe the per-token bucket** (default
975
975
  \`RateLimit-Policy: 300;w=60\`) on token requests; requests without a token
976
976
  report the per-IP bucket (\`600;w=60\`). Both are enforced.
977
- `}a(_h,"docsKnownIssues");function Eh(){return`## Error codes (JSON unless docs)
977
+ `}a(Nh,"docsKnownIssues");function Fh(){return`## Error codes (JSON unless docs)
978
978
 
979
- ${Fd()}
979
+ ${Hd()}
980
980
 
981
981
  All JSON responses use \`Cache-Control: no-store\`. Never put secrets in query
982
982
  strings you will paste publicly beyond this temporary link.
983
- `}a(Eh,"docsErrorsAndAlso");function Gd(e){let{link:t,openapiPath:n,restApiBase:r,mcpRpcPath:o,publicOrigin:s}=e,i=Nn(t.expiresAt),d=t.label?` (${Nn(t.label)})`:"",l=Nn(n),c=Nn(r),u=Nn(o);return[kh(d,i),vh(c,l,u,s?Nn(`${s}${o}`):Wd),Ah(),Sh(),xh(),Ph(),Th(),Rh(),_h(),Eh()].join(`
984
- `)}a(Gd,"renderGetLinkDocs");var rl=require("crypto");se();ie();tt();zt();Fe();et();ie();function No(e){return{error:e instanceof Error&&e.message.trim()?e.message:"Internal server error",code:"internal_error"}}a(No,"internalErrorBody");function Ch(e,t){e.headersSent||e.status(500).json(No(t))}a(Ch,"sendInternalError");function B(e,t){if(e.headersSent)return;let n=W(t);if(n){e.status(n.statusCode).json(n.body);return}Ch(e,t)}a(B,"sendCaughtRouteError");function O(e){return(t,n,r)=>{Promise.resolve().then(()=>e(t,n,r)).catch(o=>{if(n.headersSent){r(o instanceof Error?o:new Error(String(o)));return}B(n,o)})}}a(O,"asyncRoute");var Oo=require("crypto");se();ve();var G=class extends Error{static{a(this,"DraftValidationError")}statusCode=400;code;constructor(t,n="draft_invalid"){super(t),this.name="DraftValidationError",this.code=n}},Qt=class extends Error{static{a(this,"DraftNotFoundError")}statusCode=404;code="draft_not_found";constructor(t){super(`Draft '${t}' was not found or has expired`),this.name="DraftNotFoundError"}},en=class extends Error{static{a(this,"DraftAlreadySubmittedError")}statusCode=409;code="draft_already_submitted";proposalId;constructor(t,n){super(`Draft '${t}' was already submitted as proposal '${n}'`),this.name="DraftAlreadySubmittedError",this.proposalId=n}},Ih=/^[A-Za-z0-9_-]+={0,2}$/;function Kd(e){if(!Ih.test(e)||e.replace(/=+$/,"").length===0)throw new G("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data");let t;try{t=Buffer.from(e,"base64url")}catch{throw new G("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data")}if(t.byteLength===0&&e.length>0)throw new G("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data");return t}a(Kd,"assertValidBase64UrlChunk");var Fo=class{static{a(this,"DraftStore")}drafts=new Map;submitted=new Map;now;randomId;maxBytes;ttlMs;constructor(t={}){this.now=t.now??(()=>Date.now()),this.randomId=t.randomId??(()=>(0,Oo.randomBytes)(12).toString("hex")),this.maxBytes=t.maxBytes??K,this.ttlMs=t.ttlMs??ca}purgeExpired(t){for(let[n,r]of this.drafts)r.expiresAtMs<=t&&this.drafts.delete(n);for(let[n,r]of this.submitted)r.expiresAtMs<=t&&this.submitted.delete(n)}create(t){let n=this.now();this.purgeExpired(n);let r=this.randomId(),o={id:r,linkId:t.linkId,flowId:t.flowId,expectedRev:t.expectedRev,mode:t.mode,chunks:new Map,createdAtMs:n,expiresAtMs:n+this.ttlMs,totalBytes:0};return t.nodeId!==void 0&&(o.nodeId=t.nodeId),t.field!==void 0&&(o.field=t.field),t.targets!==void 0&&(o.targets=t.targets),t.summary!==void 0&&(o.summary=t.summary),t.title!==void 0&&(o.title=t.title),this.drafts.set(r,o),o}getForLink(t,n){let r=this.now();this.purgeExpired(r);let o=this.submitted.get(t);if(o?.linkId===n)throw new en(t,o.proposalId);let s=this.drafts.get(t);if(s?.linkId!==n)throw new Qt(t);if(s.expiresAtMs<=r)throw new Qt(t);return s}markSubmitted(t,n,r){let o=this.now();this.drafts.delete(t),this.submitted.set(t,{linkId:n,proposalId:r,expiresAtMs:o+this.ttlMs})}append(t,n,r,o){if(!Number.isInteger(r)||r<0)throw new G("seq must be a non-negative integer","invalid_seq");if(typeof o!="string")throw new G("data must be a base64url string","invalid_chunk");let s=this.getForLink(t,n),i=s.chunks.get(r);if(i!==void 0){if(i!==o)throw new G(`Chunk seq ${r} already stored with different data`,"chunk_conflict")}else{let u=Kd(o),p=s.totalBytes+u.byteLength;if(p>this.maxBytes)throw new G(`Draft exceeds limit of ${this.maxBytes} bytes (decoded)`,"payload_too_large");s.chunks.set(r,o),s.totalBytes=p}let d=[...s.chunks.keys()].sort((u,p)=>u-p),l=[],c=d.at(-1);if(c!==void 0)for(let u=0;u<=c;u+=1)s.chunks.has(u)||l.push(u);return{draft:s,receivedSeqs:d,missingSeqs:l,totalBytes:s.totalBytes,maxBytes:this.maxBytes}}getMaxBytes(){return this.maxBytes}assembleAndVerify(t,n,r){let o=this.getForLink(t,n);if(o.chunks.size===0)throw new G("Draft has no chunks","draft_empty");let i=[...o.chunks.keys()].sort((h,m)=>h-m).at(-1);if(i===void 0)throw new G("Draft has no chunks","draft_empty");let d=[];for(let h=0;h<=i;h+=1)o.chunks.has(h)||d.push(h);if(d.length>0)throw new G(`Missing chunk seqs: ${d.join(",")}`,"missing_chunks");let l=[];for(let h=0;h<=i;h+=1){let m=o.chunks.get(h);if(m===void 0)throw new G(`Missing chunk seqs: ${h}`,"missing_chunks");l.push(m)}let c=l.map(h=>Kd(h)),u=Buffer.concat(c);if(u.byteLength>this.maxBytes)throw new G(`Assembled draft exceeds limit of ${this.maxBytes} bytes`,"payload_too_large");let p=r.trim().toLowerCase();if(!/^[0-9a-f]{64}$/.test(p))throw new G("invalid_sha256: expected 64 hex chars","invalid_sha256");if((0,Oo.createHash)("sha256").update(u).digest("hex")!==p)throw new G("sha256 does not match assembled draft content","draft_hash_mismatch");return{draft:o,assembled:u.toString("utf8")}}size(){return this.purgeExpired(this.now()),this.drafts.size}clear(){this.drafts.clear(),this.submitted.clear()}};Ao();$t();ve();var Et=zn(require("express-rate-limit"),1);var wi=6e4,Jd=600,Vd=300;function yi(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?Math.max(1,Math.floor(e)):t}a(yi,"positiveInt");function pt(e){if(!e)return{windowMs:wi,maxPerIp:Jd,maxPerToken:Vd};let t=e;return{windowMs:yi(t.windowMs??t.rateLimitWindowMs,wi),maxPerIp:yi(t.maxPerIp??t.rateLimitMaxPerIp,Jd),maxPerToken:yi(t.maxPerToken??t.rateLimitMaxPerToken,Vd)}}a(pt,"resolveRateLimitConfig");function Lh(e,t){let r=e.rateLimit?.resetTime;return r instanceof Date&&!Number.isNaN(r.getTime())?Math.max(1,Math.ceil((r.getTime()-Date.now())/1e3)):Math.max(1,Math.ceil(t/1e3))}a(Lh,"retryAfterSeconds");function Yd(e,t,n=wi){let r=Lh(e,n);t.setHeader("Retry-After",String(r)),t.setHeader("Cache-Control","no-store"),t.status(429).json({error:"Too many requests. Slow down and retry.",code:"rate_limited",retryAfter:r,hint:"Wait retryAfter seconds, then retry. Prefer summary/search reads over full field downloads."})}a(Yd,"sendRateLimited");function Nh(e){return(t,n,r,o)=>{Yd(t,n,e)}}a(Nh,"rateLimitedHandler");function $o(e){return`ip:${(0,Et.ipKeyGenerator)(e.ip??"unknown")}`}a($o,"clientIpKey");function Mo(e){let t=e.params.token;if(typeof t=="string"&&t.length>0)return`token:${t.slice(0,48)}`;let n=e.get("authorization")??e.headers.authorization;if(typeof n=="string"){let s=/^Bearer\s+(\S+)/i.exec(n.trim())?.[1];if(s&&(s.startsWith("aia_")||s.length>=24))return`token:${s.slice(0,48)}`}let r=e;return r.accessLink?.id?`token:link:${r.accessLink.id}`:null}a(Mo,"accessTokenRateLimitKey");function Xd(e,t,n,r){let o={windowMs:t,max:e,standardHeaders:!0,legacyHeaders:!1,keyGenerator:n,handler:Nh(t)};return r&&(o.skip=r),(0,Et.default)(o)}a(Xd,"createLimiter");function bi(e){let t=pt(e);return Xd(t.maxPerIp,t.windowMs,$o)}a(bi,"createAiAccessRateLimiter");function Fh(e){let t=pt(e);return Xd(t.maxPerToken,t.windowMs,n=>Mo(n)??$o(n),n=>Mo(n)==null)}a(Fh,"createAiAccessTokenRateLimiter");function Zd(e){let t=pt(e),n=bi(t),r=Fh(t);return{ip:n,token:r,stack:[n,r],config:t}}a(Zd,"createAiAccessDualRateLimiters");function Qd(e){let t=pt(e()).windowMs,n=pt(e()).maxPerIp,r=pt(e()).maxPerToken,o=new Et.MemoryStore,s=new Et.MemoryStore,i=a(()=>{let m=pt(e());if(m.windowMs!==t||m.maxPerIp!==n||m.maxPerToken!==r){let g=m.windowMs!==t;t=m.windowMs,n=m.maxPerIp,r=m.maxPerToken,o.resetAll(),s.resetAll(),g&&(o.init?.({windowMs:t}),s.init?.({windowMs:t}))}return m},"refreshWindow"),d=new WeakSet,l=a(m=>{if(d.has(m))return;d.add(m);let g=m.setHeader.bind(m);m.setHeader=((y,A)=>(y.toLowerCase()==="ratelimit-policy"&&typeof A=="string"&&(A=A.replace(/;w=\d+/,`;w=${Math.ceil(t/1e3)}`)),g(y,A)))},"useLiveWindowPolicy"),c=a((m,g)=>(0,Et.default)({store:g,windowMs:t,limit:a(()=>{let y=pt(e());return m?y.maxPerToken:y.maxPerIp},"limit"),standardHeaders:!0,legacyHeaders:!1,keyGenerator:m?y=>Mo(y)??$o(y):$o,skip:a(y=>m&&Mo(y)==null,"skip"),handler:a((y,A,x)=>{Yd(y,A,pt(e()).windowMs)},"handler")}),"make"),u=c(!1,o),p=c(!0,s);return{ip:a((m,g,y)=>(i(),l(g),u(m,g,y)),"ip"),token:a((m,g,y)=>(i(),l(g),p(m,g,y)),"token")}}a(Qd,"createLiveAiAccessDualRateLimiters");ke();function z(e){e.setHeader("Cache-Control","no-store"),e.setHeader("Pragma","no-cache")}a(z,"setNoStore");function vi(e){let t=e.originalUrl||e.url||"",n=ks(t);n!==t&&(Object.defineProperty(e,"originalUrl",{value:n,writable:!0,configurable:!0}),typeof e.url=="string"&&Object.defineProperty(e,"url",{value:ks(e.url),writable:!0,configurable:!0}))}a(vi,"redactTokenFromRequestUrl");function el(e,t){return(n,r,o)=>{z(r);let s=n.originalUrl||n.url||"";if(n.getLinkUrlBytes=Buffer.byteLength(s,"utf8"),vi(n),!Ce(e.settings)){r.status(403).json({error:"AI Access features are disabled. Enable adminAuth, or set aiAccess.allowWithoutAdminAuth to true for local-only use.",code:"ai_features_disabled",hint:"Ask the Node-RED owner to enable AI Access."});return}let i=n.params.token;if(typeof i!="string"||i.length===0){r.setHeader("WWW-Authenticate",ce),r.status(401).json({error:xe,code:qe,hint:"Ask the user for a new self-contained access link URL."});return}let d=i;try{d=decodeURIComponent(i)}catch{}let l=t.validateResult(d);if(l.status==="expired"){tr(n,r,e.settings.httpAdminRoot,l.expiresAt);return}if(l.status!=="active"){let c=d.length<24||d.startsWith("aia_")&&d.length<40?"The access-link URL may be truncated \u2014 ask the user for the full /mcp/a/<token> link.":"Ask the user for a new self-contained access link URL (it may have expired, or Node-RED may have restarted).";r.setHeader("WWW-Authenticate",ce),r.status(401).json({error:Ot,code:er,hint:c});return}if(l.link.kind!=="get_link"){nr(r,"get_link",l.link.kind);return}n.accessLink=l.link,n.getLinkToken=d,o()}}a(el,"requireGetLinkToken");function Fn(e,t){let n=e.getLinkUrlBytes;return(typeof n=="number"?n:Buffer.byteLength(e.originalUrl||e.url||"","utf8"))>gn?(z(t),t.status(414).json({error:`Request URL exceeds ${gn} bytes`,code:"url_too_long",hint:"Use chunked drafts: GET \u2026/draft/new then \u2026/draft/<id>/append?seq=&data= then \u2026/draft/<id>/submit?sha256=, or use POST /mcp/api/v1/proposals with Authorization: Bearer <token>.",limitBytes:gn}),!0):!1}a(Fn,"rejectOversizedGetUrl");function j(e,t,n,r,o,s){z(e),e.status(t).json({error:n,code:r,...o?{hint:o}:{},...s})}a(j,"jsonError");function tl(){return(e,t)=>{vi(e),z(t),t.status(404).json({error:"Not found",code:"not_found"})}}a(tl,"getLinkNotFoundHandler");var Oh="link_create_not_supported";function nl(){return(e,t)=>{vi(e),z(t),t.status(403).json({error:"GET access links cannot create tokens",code:Oh,hint:"Ask an admin with * permission to mint tokens via POST /mcp/api/v1/links on the /ai-access dashboard (Access tokens tab). This get_link URL cannot create, list, or revoke tokens."})}}a(nl,"getLinkMintNotSupportedHandler");function M(e,t){let n=e[t];if(typeof n=="string"&&n.length>0)return n;if(Array.isArray(n)&&typeof n[0]=="string"&&n[0].length>0)return n[0]}a(M,"queryString");function He(e){switch(e){case"draft_not_found":return"Start a new draft with GET \u2026/draft/new.";case"draft_already_submitted":return"This draft already created a proposal; poll that proposal or start a new draft.";case"chunk_conflict":return"Re-send the same data for that seq, or start a new draft.";case"payload_too_large":return"Shrink the content; totalBytes counts decoded bytes against maxBytes.";case"invalid_seq":return"seq must be a non-negative integer (0, 1, 2, \u2026).";case"invalid_chunk":case"invalid_chunk_data":return"data must be base64url of the raw chunk bytes (A-Z, a-z, 0-9, -, _).";case"missing_chunks":return"Upload every seq from 0 through max before submit.";case"invalid_sha256":return"sha256 must be exactly 64 hexadecimal characters.";case"draft_hash_mismatch":return"sha256 must be the hex digest of the assembled UTF-8 bytes (before base64url).";case"draft_empty":return"Append at least one chunk before submit.";case"node_not_found":return"Call GET \u2026/flows/<id> and use a node id from that response (or use mode=json with op:add).";case"field_not_found":return"Use a property that exists on the node, or mode=json to add/replace nodes.";case"missing_draft_params":return"Example: \u2026/draft/new?flow=tab1&rev=abc&node=n1&field=template";case"version_mismatch":return"Re-read the flow and start a new draft with the current rev.";case"protected_property":return"Omit id, type, and z from set props. wires are allowed when targets exist in the flow.";case"invalid_wires":return"wires must list existing node ids (or add those nodes in the same patch).";default:return}}a(He,"draftErrorHint");async function $h(e,t,n){let r;try{r=await e.getFlow(t)}catch(s){let i=W(s);if(i)return{code:i.body.code,message:i.body.error};throw s}let o=[...Array.isArray(r.flow.nodes)?r.flow.nodes:[],...Array.isArray(r.flow.configs)?r.flow.configs:[]];for(let s of n){let i=o.find(d=>d.id===s.nodeId);if(!i)return{code:"node_not_found",message:`Node '${s.nodeId}' was not found in flow '${t}'`};if(!(s.field in i))return{code:"field_not_found",message:`Field '${s.field}' was not found on node '${s.nodeId}'`}}return null}a($h,"validateFieldDraftTargets");function Mh(e,t,n,r){if(r)try{let o=vr(r);if(!Array.isArray(o)||o.length===0)throw new Error("targets must be a non-empty array");return{ok:!0,mode:"field",targets:o.map(i=>{if(!i||typeof i!="object")throw new Error("bad target");let d=i,l=typeof d.nodeId=="string"?d.nodeId:typeof d.node=="string"?d.node:"",c=typeof d.field=="string"?d.field:"";if(!l||!c)throw new Error("bad target");return{nodeId:l,field:c}})}}catch{return{ok:!1,status:400,error:"targets must be base64url JSON array of {nodeId,field}",code:"invalid_targets"}}if(n==="json"||!e&&!t)return{ok:!0,mode:"json"};if(!e||!t){let o=He("missing_draft_params");return{ok:!1,status:400,error:"node and field are required for field drafts (or pass mode=json)",code:"missing_draft_params",...o?{hint:o}:{}}}return{ok:!0,mode:"field"}}a(Mh,"planDraftMode");async function Dh(e,t,n,r){try{let o=await $h(t,n,r);if(o){let s=o.code==="node_not_found"||o.code==="field_not_found"?404:400;return j(e,s,o.message,o.code,He(o.code)),!0}}catch(o){let s=W(o);return s?(j(e,s.statusCode,s.body.error,s.body.code,He(s.body.code)),!0):(B(e,o),!0)}return!1}a(Dh,"rejectInvalidFieldTargets");function ol(e,t){return async(n,r)=>{if(Fn(n,r))return;z(r);let o=M(n.query,"flow"),s=M(n.query,"rev"),i=M(n.query,"node"),d=M(n.query,"field"),l=M(n.query,"summary"),c=M(n.query,"title")??l,u=M(n.query,"mode"),p=M(n.query,"targets");if(!o||!s){j(r,400,"flow and rev query parameters are required","missing_draft_params",He("missing_draft_params"));return}if(!n.accessLink){j(r,401,"Access link required","access_link_missing");return}try{let m=await(await Ar(t,s)).getFlow(o);if(m.rev!==s){r.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:s,currentRev:m.rev,hint:He("version_mismatch")});return}}catch(m){let g=W(m);if(g){j(r,g.statusCode,g.body.error,g.body.code,g.body.code==="flow_not_found"?we("flow_not_found","get")??"Use an id from GET ./flows":He(g.body.code));return}B(r,m);return}let f=Mh(i,d,u,p);if(!f.ok){j(r,f.status,f.error,f.code,f.hint);return}if(f.mode==="field"){let m=f.targets&&f.targets.length>0?f.targets:i&&d?[{nodeId:i,field:d}]:[];if(m.length>0&&await Dh(r,t,o,m))return}let h=e.create({linkId:n.accessLink.id,flowId:o,expectedRev:s,mode:f.mode,...i?{nodeId:i}:{},...d?{field:d}:{},...f.targets?{targets:f.targets}:{},...l?{summary:l}:{},...c?{title:c}:{}});r.status(201).json({ok:!0,draftId:h.id,mode:h.mode,expiresAt:new Date(h.expiresAtMs).toISOString(),maxBytes:K,hint:"Upload base64url chunks (~4\u20135 KB raw each; base64 adds ~33%; keep URL under ~8 KB) with GET \u2026/draft/<draftId>/append?seq=N&data=\u2026 then submit with sha256."})}}a(ol,"getLinkDraftNewHandler");function sl(e){return(t,n)=>{if(Fn(t,n))return;z(n);let r=t.params.draftId,o=M(t.query,"seq"),s=M(t.query,"data");if(typeof r!="string"||!r){j(n,400,"Missing draft id","missing_draft_id");return}if(!t.accessLink){j(n,401,"Access link required","access_link_missing");return}if(o===void 0||s===void 0){j(n,400,"seq and data query parameters are required","missing_chunk_params","data must be base64url of the raw chunk bytes.");return}let i=Number(o);try{let d=e.append(r,t.accessLink.id,i,s);n.json({ok:!0,draftId:r,receivedSeqs:d.receivedSeqs,missingSeqs:d.missingSeqs,totalBytes:d.totalBytes,maxBytes:d.maxBytes,hint:d.missingSeqs.length===0?"No gaps so far; call submit when all chunks are uploaded":`Upload missing seqs: ${d.missingSeqs.join(",")}`})}catch(d){if(d instanceof Qt||d instanceof en||d instanceof G){j(n,d.statusCode,d.message,d.code,He(d.code),d instanceof en?{proposalId:d.proposalId}:void 0);return}B(n,d);return}}}a(sl,"getLinkDraftAppendHandler");function Hh(e,t){let n=[];if(e.targets&&e.targets.length>0){let r;try{r=JSON.parse(t)}catch{let o=e.targets[0];if(e.targets.length===1&&o)return[{nodeId:o.nodeId,set:{[o.field]:t}}];throw new G("Multi-target draft body must be JSON","invalid_draft_body")}if(Array.isArray(r)){for(let o of r){let s=o;if(!s.nodeId||!s.field)throw new G("Invalid multi-target entry","invalid_draft_body");n.push({nodeId:s.nodeId,set:{[s.field]:s.value}})}return n}if(r&&typeof r=="object"){let o=r;for(let s of e.targets){let i=`${s.nodeId}.${s.field}`;if(!(i in o)&&!(s.field in o))throw new G(`Missing value for ${i} in draft JSON`,"invalid_draft_body");n.push({nodeId:s.nodeId,set:{[s.field]:i in o?o[i]:o[s.field]}})}return n}throw new G("Invalid multi-target draft JSON","invalid_draft_body")}if(e.nodeId&&e.field)return[{nodeId:e.nodeId,set:{[e.field]:t}}];throw new G("Field draft missing node/field","invalid_draft")}a(Hh,"editsFromFieldDraft");function il(e,t,n,r,o,s){return async(i,d)=>{if(Fn(i,d))return;z(d);let l=i.params.draftId,c=M(i.query,"sha256"),u=M(i.query,"title");if(typeof l!="string"||!l){j(d,400,"Missing draft id","missing_draft_id");return}if(!i.accessLink){j(d,401,"Access link required","access_link_missing");return}if(!c){j(d,400,"sha256 query parameter is required","missing_sha256","Pass sha256 hex of the assembled UTF-8 bytes (before base64url).");return}try{let{draft:p,assembled:f}=e.assembleAndVerify(l,i.accessLink.id,c),h=p.flowId,m,g=p.expectedRev,y=await Ar(r,p.expectedRev);if(p.mode==="field"){let k=Hh(p,f),_=await br(y,p.flowId,k);if(_.expectedRev!==p.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:p.expectedRev,currentRev:_.expectedRev,draftDeleted:!1,hint:"Draft was kept. Re-read the flow and start a new draft with the current rev (or retry if you refresh rev)."});return}m=_.flow,g=_.expectedRev}else{let k=kr(JSON.parse(f),p.flowId);if(h=k.flowId,k.kind==="replace_flow")m={...k.flow,id:k.flow.id||h,type:k.flow.type??"tab",nodes:k.flow.nodes??[]};else{let _=await Gt(y,h,k.ops,{expectedRev:p.expectedRev,...s?{nodeTypes:s}:{}});if(_.expectedRev!==p.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:p.expectedRev,currentRev:_.expectedRev,draftDeleted:!1,hint:"Draft was kept. Re-read the flow and start a new draft with the current rev."});return}m=_.flow,g=_.expectedRev}}let A=u??p.title??p.summary,x=V(i,o),$=ge(i.query),L=await Tt(t,n,i.accessLink,{type:"replace_flow",expectedRev:g,payload:{flowId:h,flow:m},...A?{title:A}:{}},r,x,s);e.markSubmitted(l,i.accessLink.id,L.id);let D={...fe(L,{...x,include:$}),mine:!0},H=ne(t),T=Ve(t,t.get(L.id),i.accessLink.id);d.status(201).json({ok:!0,proposal:D,sha256:(0,rl.createHash)("sha256").update(Buffer.from(f,"utf8")).digest("hex"),...Ye(T),...ue(H),hint:`Waiting for human approval. Tell the user about "${D.displayName}" (${D.reviewUrl}). Poll GET \u2026/proposals/<id> for status.`})}catch(p){if(p instanceof en){j(d,p.statusCode,p.message,p.code,He(p.code),{proposalId:p.proposalId});return}if(p instanceof Qt||p instanceof G){j(d,p.statusCode,p.message,p.code,He(p.code));return}if(p instanceof SyntaxError){j(d,400,"Assembled draft is not valid JSON","invalid_draft_body");return}let f=W(p);if(f){d.status(f.statusCode).json({...f.body,...He(f.body.code)?{hint:He(f.body.code)}:{}});return}B(d,p);return}}}a(il,"getLinkDraftSubmitHandler");ie();tt();_o();zt();Fe();et();Ao();var jh={node_already_exists:"Choose a new node id, or use op:set / op:remove on the existing node.",protected_property:"Omit id, type, and z from set props. wires are allowed when targets exist in the flow.",invalid_wires:"wires must list existing node ids (or add those nodes in the same patch).",invalid_patch:"Pass patch as base64url JSON with nodes[{op,\u2026.}] or type/flow for replace_flow.",invalid_patch_json:"Pass patch as base64url JSON with nodes[{op,\u2026.}] or type/flow for replace_flow.",missing_edits:"Provide set.<prop>=value, find+replace+field, or patch nodes with at least one op.",missing_node_id:"Pass node=<id> with set.<prop>= or find/replace, or include id on each patch.nodes entry.",missing_flow_id:"Pass flow=<id> from GET \u2026/flows.",missing_field:"Pass field=<prop> (e.g. template, func) with find= and replace=.",missing_find:"Pass a non-empty find= string (URL-encoded).",invalid_occurrence:"occurrence must be a 1-based index within the match count.",invalid_replace:"Pass replace= as a string (URL-encoded; empty string is allowed to delete the find text).",conflicting_edits:"Use either find/replace/field or set.<prop>=, not both in one propose URL.",invalid_node_type:"node.type must be a string (not a number or object). Use the palette type name exactly.",invalid_node:"add op needs node as a JSON object: { id, type, \u2026 } \u2014 not a string or array.",z_mismatch:"Each node.z must match the flow id (tab id) you are editing."};function ki(e,t){return we(e,"get",{cause:t})??jh[e]??Tr(e)}a(ki,"proposeErrorHint");function Uh(e,t,n){e.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:t,currentRev:n,hint:ki("version_mismatch")})}a(Uh,"sendVersionMismatch");async function Bh(e,t,n,r,o){let s=kr(vr(t),n),i=s.flowId;if(s.kind==="replace_flow")return{ok:!0,builtFlowId:i,builtFlow:{...s.flow,id:s.flow.id||i,type:s.flow.type??"tab",nodes:s.flow.nodes??[]},expectedRev:r};let d=await Gt(e,i,s.ops,{expectedRev:r,...o?{nodeTypes:o}:{}});return d.expectedRev!==r?{ok:!1,kind:"version_mismatch",expectedRev:r,currentRev:d.expectedRev}:{ok:!0,builtFlowId:i,builtFlow:d.flow,expectedRev:d.expectedRev}}a(Bh,"buildFromPatch");async function qh(e,t,n,r,o){let s=await br(e,t,[{nodeId:n,set:r}]);return s.expectedRev!==o?{ok:!1,kind:"version_mismatch",expectedRev:o,currentRev:s.expectedRev}:{ok:!0,builtFlowId:t,builtFlow:s.flow,expectedRev:s.expectedRev}}a(qh,"buildFromSetProps");async function zh(e,t,n,r,o,s,i,d){let l=await Gt(e,t,[{op:"replace_text",nodeId:n,field:r,find:o,replace:s,...i!==void 0?{occurrence:i}:{}}]);return l.expectedRev!==d?{ok:!1,kind:"version_mismatch",expectedRev:d,currentRev:l.expectedRev}:{ok:!0,builtFlowId:t,builtFlow:l.flow,expectedRev:l.expectedRev}}a(zh,"buildFromReplaceText");function Wh(e){let t=Object.prototype.hasOwnProperty.call(e.query,"replace");return{flowId:M(e.query,"flow"),rev:M(e.query,"rev"),nodeId:M(e.query,"node"),patchEncoded:M(e.query,"patch"),setProps:ii(e.query),findText:M(e.query,"find"),replaceText:t?M(e.query,"replace")??"":void 0,fieldName:M(e.query,"field"),occurrenceRaw:M(e.query,"occurrence")}}a(Wh,"readProposeQuery");function je(e,t,n){j(e,400,n,t,ki(t))}a(je,"jsonProposeError");function Gh(e,t){if(t===void 0||t==="")return{ok:!0};let n=Number(t);return Number.isFinite(n)?{ok:!0,occurrence:Math.trunc(n)}:(je(e,"invalid_occurrence","occurrence must be a positive integer"),{ok:!1})}a(Gh,"parseOccurrenceQuery");async function Kh(e,t,n,r,o){if(n.patchEncoded)return Bh(t,n.patchEncoded,n.flowId,r,o);if(n.findText!==void 0||n.replaceText!==void 0||n.fieldName!==void 0){if(Object.keys(n.setProps).length>0)return je(e,"conflicting_edits","Cannot combine replace_text (find/replace/field) with set.<prop> in one request"),null;if(!n.flowId)return je(e,"missing_flow_id","flow query parameter is required"),null;if(!n.nodeId)return je(e,"missing_node_id","node query parameter is required"),null;if(!n.fieldName)return je(e,"missing_field","field query parameter is required for replace_text"),null;if(n.findText===void 0||n.findText.length===0)return je(e,"missing_find","find query parameter must be a non-empty string"),null;if(n.replaceText===void 0)return je(e,"invalid_replace","replace query parameter is required (may be empty)"),null;let i=Gh(e,n.occurrenceRaw);return i.ok?zh(t,n.flowId,n.nodeId,n.fieldName,n.findText,n.replaceText,i.occurrence,r):null}return n.flowId?n.nodeId?Object.keys(n.setProps).length===0?(je(e,"missing_edits","At least one set.<prop>=value is required"),null):qh(t,n.flowId,n.nodeId,n.setProps,r):(je(e,"missing_node_id","node query parameter is required (or pass patch=)"),null):(je(e,"missing_flow_id","flow query parameter is required"),null)}a(Kh,"buildProposeFromQuery");function al(e,t,n,r,o){return async(s,i)=>{if(Fn(s,i))return;z(i);let d=Wh(s),l=M(s.query,"summary"),c=M(s.query,"title")??l;if(!d.rev){je(i,"missing_expected_rev","rev query parameter is required");return}if(!s.accessLink){j(i,401,"Access link required","access_link_missing");return}try{let u=await Ar(n,d.rev),p=await Kh(i,u,d,d.rev,o);if(!p)return;if(!p.ok){Uh(i,p.expectedRev,p.currentRev);return}let f=V(s,r),h=ge(s.query),m=await Tt(e,t,s.accessLink,{type:"replace_flow",expectedRev:p.expectedRev,payload:{flowId:p.builtFlowId,flow:p.builtFlow},...c?{title:c}:{}},n,f,o),g={...fe(m,{...f,include:h}),mine:!0},y=ne(e),A=Ve(e,e.get(m.id),s.accessLink.id),x=m.warnings;i.status(201).json({ok:!0,proposal:g,...l?{summary:l}:{},...Ye(A),...ue(y),...x&&x.length>0?{warnings:x}:{},hint:`Waiting for human approval. Tell the user about "${g.displayName}" (${g.reviewUrl}). Poll GET \u2026/proposals/<id> for status.${x&&x.length>0?` Soft warnings (${x.length}) are non-blocking \u2014 configure later if needed.`:""}`})}catch(u){let p=W(u,"get");if(p){let f=ki(p.body.code,p.body.cause);i.status(p.statusCode).json({...p.body,...f?{hint:f}:{}});return}B(i,u)}}}a(al,"getLinkProposeHandler");ie();tt();ie();var Jh=50,dl=40,Vh=20;function Yh(e){return Buffer.byteLength(JSON.stringify(e),"utf8")}a(Yh,"nodeBytes");function Xh(e){let t={id:e.id,type:e.type,bytes:Yh(e)};return typeof e.name=="string"&&(t.name=e.name),"wires"in e&&(t.wires=e.wires),t}a(Xh,"summarizeNode");async function Do(e,t){let{rev:n,flow:r}=await Me(e,t),o=r,s=Array.isArray(o.nodes)?o.nodes.map(Xh):[],i={rev:n,flowId:o.id||t,nodes:s,nodeCount:s.length};return typeof o.label=="string"&&(i.label=o.label),typeof o.type=="string"&&(i.type=o.type),i}a(Do,"getFlowSummary");function ll(e){let t=[];for(let n of e){if(!n||typeof n!="object"||Array.isArray(n))continue;let r=n;(r.type==="tab"||r.type==="subflow"||Array.isArray(r.nodes))&&t.push(r)}return t}a(ll,"collectTabs");async function cl(e,t,n){if(!t)throw new b("Missing node id","missing_node_id");if(n){let{rev:d,flow:l}=await Me(e,n),c=l,u=Array.isArray(c.nodes)?c.nodes:[],p=Array.isArray(c.configs)?c.configs:[],f=[...u,...p].find(h=>h.id===t);if(!f)throw new b("Node was not found","node_not_found",{statusCode:404});return{rev:d,flowId:n,node:f}}let{rev:r,flows:o}=await ut(e),s,i=null;for(let d of o){if(!d||typeof d!="object"||Array.isArray(d))continue;let l=d;if(l.id===t&&typeof l.type=="string"&&l.type!=="tab"&&l.type!=="subflow"){s=l,i=typeof l.z=="string"&&l.z?l.z:null;break}}if(!s)for(let d of ll(o)){let l=d.id;if(l)try{let u=(await Me(e,l)).flow,f=[...u.nodes??[],...u.configs??[]].find(h=>h.id===t);if(f){s=f,i=l;break}}catch{}}if(!s)throw new b("Node was not found","node_not_found",{statusCode:404});return{rev:r,flowId:i,node:s}}a(cl,"findNodeInFlows");async function Ho(e,t,n){let r=await cl(e,t,n);return{rev:r.rev,flowId:r.flowId,node:r.node}}a(Ho,"getNodeById");function Zh(e){if(e===void 0||e==="")return;let t=/^(\d+)(?:-(\d+))?$/.exec(e.trim());if(!t)throw new b("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");let n=Number(t[1]),r=t[2]!==void 0?Number(t[2]):n;if(!Number.isInteger(n)||!Number.isInteger(r)||n<1)throw new b("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");if(r<n)throw new b("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");return{start:n,end:r}}a(Zh,"parseLineRange");async function jo(e,t,n,r={}){if(!n)throw new b("Missing field name","missing_field");let o=await cl(e,t,r.flowId);if(!(n in o.node))throw new b("Field was not found on the node","field_not_found");let s=o.node[n];if(typeof s!="string")throw new b("Field is not a string","field_not_string");let i=s.split(`
985
- `),d=i.length,l=Buffer.byteLength(s,"utf8"),c=Zh(r.lines);if(!c)return{rev:o.rev,flowId:o.flowId,nodeId:t,field:n,value:s,totalLines:d,bytes:l};if(c.start>d)throw new b(`lines range is out of bounds (start ${c.start} > ${d} total lines)`,"line_range_out_of_bounds",{details:{totalLines:d}});let u=c.start-1,p=Math.min(c.end,i.length),f=i.slice(u,p).join(`
986
- `);return{rev:o.rev,flowId:o.flowId,nodeId:t,field:n,value:f,totalLines:d,bytes:l,lineRange:{start:c.start,end:Math.min(c.end,d)}}}a(jo,"getNodeField");function Qh(e){let t=[];for(let[n,r]of Object.entries(e))n==="id"||n==="type"||n==="z"||n==="wires"||typeof r=="string"&&r.length>0&&t.push({field:n,value:r});return t}a(Qh,"stringFields");function em(e,t,n){let r=Math.max(0,t-dl),o=Math.min(e.length,t+n+dl),s=e.slice(r,o).replace(/\s+/g," ");return r>0&&(s=`\u2026${s}`),o<e.length&&(s=`${s}\u2026`),s}a(em,"snippetAround");function tm(e,t){let n=1;for(let r=0;r<t&&r<e.length;r+=1)e.charCodeAt(r)===10&&(n+=1);return n}a(tm,"lineNumberAtIndex");function Ai(e,t,n,r,o){for(let{field:s,value:i}of Qh(e)){if(r.length>=o)return!0;let d=[],l=new Set,c=-1,u=a((f,h)=>{l.has(f)||(l.add(f),d.push(f)),c<0&&(c=h)},"pushLine");if(!n.includes(`
983
+ `}a(Fh,"docsErrorsAndAlso");function Xd(e){let{link:t,openapiPath:n,restApiBase:r,mcpRpcPath:o,publicOrigin:s}=e,i=Hn(t.expiresAt),d=t.label?` (${Hn(t.label)})`:"",l=Hn(n),c=Hn(r),u=Hn(o);return[Rh(d,i),Th(c,l,u,s?Hn(`${s}${o}`):Yd),Ph(),_h(),Ch(),Lh(),Eh(),Ih(),Nh(),Fh()].join(`
984
+ `)}a(Xd,"renderGetLinkDocs");var dl=require("crypto");oe();se();rt();Wt();Oe();nt();se();function $o(e){return{error:e instanceof Error&&e.message.trim()?e.message:"Internal server error",code:"internal_error"}}a($o,"internalErrorBody");function Oh(e,t){e.headersSent||e.status(500).json($o(t))}a(Oh,"sendInternalError");function B(e,t){if(e.headersSent)return;let n=W(t);if(n){e.status(n.statusCode).json(n.body);return}Oh(e,t)}a(B,"sendCaughtRouteError");function $(e){return(t,n,r)=>{Promise.resolve().then(()=>e(t,n,r)).catch(o=>{if(n.headersSent){r(o instanceof Error?o:new Error(String(o)));return}B(n,o)})}}a($,"asyncRoute");var Do=require("crypto");oe();ve();var G=class extends Error{static{a(this,"DraftValidationError")}statusCode=400;code;constructor(t,n="draft_invalid"){super(t),this.name="DraftValidationError",this.code=n}},Qt=class extends Error{static{a(this,"DraftNotFoundError")}statusCode=404;code="draft_not_found";constructor(t){super(`Draft '${t}' was not found or has expired`),this.name="DraftNotFoundError"}},en=class extends Error{static{a(this,"DraftAlreadySubmittedError")}statusCode=409;code="draft_already_submitted";proposalId;constructor(t,n){super(`Draft '${t}' was already submitted as proposal '${n}'`),this.name="DraftAlreadySubmittedError",this.proposalId=n}},$h=/^[A-Za-z0-9_-]+={0,2}$/;function Zd(e){if(!$h.test(e)||e.replace(/=+$/,"").length===0)throw new G("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data");let t;try{t=Buffer.from(e,"base64url")}catch{throw new G("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data")}if(t.byteLength===0&&e.length>0)throw new G("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data");return t}a(Zd,"assertValidBase64UrlChunk");var Mo=class{static{a(this,"DraftStore")}drafts=new Map;submitted=new Map;now;randomId;maxBytes;ttlMs;constructor(t={}){this.now=t.now??(()=>Date.now()),this.randomId=t.randomId??(()=>(0,Do.randomBytes)(12).toString("hex")),this.maxBytes=t.maxBytes??K,this.ttlMs=t.ttlMs??ga}purgeExpired(t){for(let[n,r]of this.drafts)r.expiresAtMs<=t&&this.drafts.delete(n);for(let[n,r]of this.submitted)r.expiresAtMs<=t&&this.submitted.delete(n)}create(t){let n=this.now();this.purgeExpired(n);let r=this.randomId(),o={id:r,linkId:t.linkId,flowId:t.flowId,expectedRev:t.expectedRev,mode:t.mode,chunks:new Map,createdAtMs:n,expiresAtMs:n+this.ttlMs,totalBytes:0};return t.nodeId!==void 0&&(o.nodeId=t.nodeId),t.field!==void 0&&(o.field=t.field),t.targets!==void 0&&(o.targets=t.targets),t.summary!==void 0&&(o.summary=t.summary),t.title!==void 0&&(o.title=t.title),this.drafts.set(r,o),o}getForLink(t,n){let r=this.now();this.purgeExpired(r);let o=this.submitted.get(t);if(o?.linkId===n)throw new en(t,o.proposalId);let s=this.drafts.get(t);if(s?.linkId!==n)throw new Qt(t);if(s.expiresAtMs<=r)throw new Qt(t);return s}markSubmitted(t,n,r){let o=this.now();this.drafts.delete(t),this.submitted.set(t,{linkId:n,proposalId:r,expiresAtMs:o+this.ttlMs})}append(t,n,r,o){if(!Number.isInteger(r)||r<0)throw new G("seq must be a non-negative integer","invalid_seq");if(typeof o!="string")throw new G("data must be a base64url string","invalid_chunk");let s=this.getForLink(t,n),i=s.chunks.get(r);if(i!==void 0){if(i!==o)throw new G(`Chunk seq ${r} already stored with different data`,"chunk_conflict")}else{let u=Zd(o),p=s.totalBytes+u.byteLength;if(p>this.maxBytes)throw new G(`Draft exceeds limit of ${this.maxBytes} bytes (decoded)`,"payload_too_large");s.chunks.set(r,o),s.totalBytes=p}let d=[...s.chunks.keys()].sort((u,p)=>u-p),l=[],c=d.at(-1);if(c!==void 0)for(let u=0;u<=c;u+=1)s.chunks.has(u)||l.push(u);return{draft:s,receivedSeqs:d,missingSeqs:l,totalBytes:s.totalBytes,maxBytes:this.maxBytes}}getMaxBytes(){return this.maxBytes}assembleAndVerify(t,n,r){let o=this.getForLink(t,n);if(o.chunks.size===0)throw new G("Draft has no chunks","draft_empty");let i=[...o.chunks.keys()].sort((m,h)=>m-h).at(-1);if(i===void 0)throw new G("Draft has no chunks","draft_empty");let d=[];for(let m=0;m<=i;m+=1)o.chunks.has(m)||d.push(m);if(d.length>0)throw new G(`Missing chunk seqs: ${d.join(",")}`,"missing_chunks");let l=[];for(let m=0;m<=i;m+=1){let h=o.chunks.get(m);if(h===void 0)throw new G(`Missing chunk seqs: ${m}`,"missing_chunks");l.push(h)}let c=l.map(m=>Zd(m)),u=Buffer.concat(c);if(u.byteLength>this.maxBytes)throw new G(`Assembled draft exceeds limit of ${this.maxBytes} bytes`,"payload_too_large");let p=r.trim().toLowerCase();if(!/^[0-9a-f]{64}$/.test(p))throw new G("invalid_sha256: expected 64 hex chars","invalid_sha256");if((0,Do.createHash)("sha256").update(u).digest("hex")!==p)throw new G("sha256 does not match assembled draft content","draft_hash_mismatch");return{draft:o,assembled:u.toString("utf8")}}size(){return this.purgeExpired(this.now()),this.drafts.size}clear(){this.drafts.clear(),this.submitted.clear()}};Ro();Mt();ve();var Ct=Vn(require("express-rate-limit"),1);var Ti=6e4,Qd=600,el=300;function Si(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?Math.max(1,Math.floor(e)):t}a(Si,"positiveInt");function ft(e){if(!e)return{windowMs:Ti,maxPerIp:Qd,maxPerToken:el};let t=e;return{windowMs:Si(t.windowMs??t.rateLimitWindowMs,Ti),maxPerIp:Si(t.maxPerIp??t.rateLimitMaxPerIp,Qd),maxPerToken:Si(t.maxPerToken??t.rateLimitMaxPerToken,el)}}a(ft,"resolveRateLimitConfig");function Mh(e,t){let r=e.rateLimit?.resetTime;return r instanceof Date&&!Number.isNaN(r.getTime())?Math.max(1,Math.ceil((r.getTime()-Date.now())/1e3)):Math.max(1,Math.ceil(t/1e3))}a(Mh,"retryAfterSeconds");function tl(e,t,n=Ti){let r=Mh(e,n);t.setHeader("Retry-After",String(r)),t.setHeader("Cache-Control","no-store"),t.status(429).json({error:"Too many requests. Slow down and retry.",code:"rate_limited",retryAfter:r,hint:"Wait retryAfter seconds, then retry. Prefer summary/search reads over full field downloads."})}a(tl,"sendRateLimited");function Dh(e){return(t,n,r,o)=>{tl(t,n,e)}}a(Dh,"rateLimitedHandler");function Ho(e){return`ip:${(0,Ct.ipKeyGenerator)(e.ip??"unknown")}`}a(Ho,"clientIpKey");function jo(e){let t=e.params.token;if(typeof t=="string"&&t.length>0)return`token:${t.slice(0,48)}`;let n=e.get("authorization")??e.headers.authorization;if(typeof n=="string"){let s=/^Bearer\s+(\S+)/i.exec(n.trim())?.[1];if(s&&(s.startsWith("aia_")||s.length>=24))return`token:${s.slice(0,48)}`}let r=e;return r.accessLink?.id?`token:link:${r.accessLink.id}`:null}a(jo,"accessTokenRateLimitKey");function nl(e,t,n,r){let o={windowMs:t,max:e,standardHeaders:!0,legacyHeaders:!1,keyGenerator:n,handler:Dh(t)};return r&&(o.skip=r),(0,Ct.default)(o)}a(nl,"createLimiter");function Ri(e){let t=ft(e);return nl(t.maxPerIp,t.windowMs,Ho)}a(Ri,"createAiAccessRateLimiter");function Hh(e){let t=ft(e);return nl(t.maxPerToken,t.windowMs,n=>jo(n)??Ho(n),n=>jo(n)==null)}a(Hh,"createAiAccessTokenRateLimiter");function rl(e){let t=ft(e),n=Ri(t),r=Hh(t);return{ip:n,token:r,stack:[n,r],config:t}}a(rl,"createAiAccessDualRateLimiters");function ol(e){let t=ft(e()).windowMs,n=ft(e()).maxPerIp,r=ft(e()).maxPerToken,o=new Ct.MemoryStore,s=new Ct.MemoryStore,i=a(()=>{let h=ft(e());if(h.windowMs!==t||h.maxPerIp!==n||h.maxPerToken!==r){let g=h.windowMs!==t;t=h.windowMs,n=h.maxPerIp,r=h.maxPerToken,o.resetAll(),s.resetAll(),g&&(o.init?.({windowMs:t}),s.init?.({windowMs:t}))}return h},"refreshWindow"),d=new WeakSet,l=a(h=>{if(d.has(h))return;d.add(h);let g=h.setHeader.bind(h);h.setHeader=((y,w)=>(y.toLowerCase()==="ratelimit-policy"&&typeof w=="string"&&(w=w.replace(/;w=\d+/,`;w=${Math.ceil(t/1e3)}`)),g(y,w)))},"useLiveWindowPolicy"),c=a((h,g)=>(0,Ct.default)({store:g,windowMs:t,limit:a(()=>{let y=ft(e());return h?y.maxPerToken:y.maxPerIp},"limit"),standardHeaders:!0,legacyHeaders:!1,keyGenerator:h?y=>jo(y)??Ho(y):Ho,skip:a(y=>h&&jo(y)==null,"skip"),handler:a((y,w,v)=>{tl(y,w,ft(e()).windowMs)},"handler")}),"make"),u=c(!1,o),p=c(!0,s);return{ip:a((h,g,y)=>(i(),l(g),u(h,g,y)),"ip"),token:a((h,g,y)=>(i(),l(g),p(h,g,y)),"token")}}a(ol,"createLiveAiAccessDualRateLimiters");ke();function z(e){e.setHeader("Cache-Control","no-store"),e.setHeader("Pragma","no-cache")}a(z,"setNoStore");function Pi(e){let t=e.originalUrl||e.url||"",n=Ss(t);n!==t&&(Object.defineProperty(e,"originalUrl",{value:n,writable:!0,configurable:!0}),typeof e.url=="string"&&Object.defineProperty(e,"url",{value:Ss(e.url),writable:!0,configurable:!0}))}a(Pi,"redactTokenFromRequestUrl");function sl(e,t){return(n,r,o)=>{z(r);let s=n.originalUrl||n.url||"";if(n.getLinkUrlBytes=Buffer.byteLength(s,"utf8"),Pi(n),!Ee(e.settings)){r.status(403).json({error:"AI Access features are disabled. Enable adminAuth, or set aiAccess.allowWithoutAdminAuth to true for local-only use.",code:"ai_features_disabled",hint:"Ask the Node-RED owner to enable AI Access."});return}let i=n.params.token;if(typeof i!="string"||i.length===0){r.setHeader("WWW-Authenticate",fe),r.status(401).json({error:Ie,code:at,hint:"Ask the user for a new self-contained access link URL."});return}let d=i;try{d=decodeURIComponent(i)}catch{}let l=t.validateResult(d);if(l.status==="expired"){sr(n,r,e.settings.httpAdminRoot,l.expiresAt);return}if(l.status!=="active"){let c=d.length<24||d.startsWith("aia_")&&d.length<40?"The access-link URL may be truncated \u2014 ask the user for the full /mcp/a/<token> link.":"Ask the user for a new self-contained access link URL (it may have expired, or Node-RED may have restarted).";r.setHeader("WWW-Authenticate",fe),r.status(401).json({error:$t,code:or,hint:c});return}if(l.link.kind!=="get_link"){ir(r,"get_link",l.link.kind);return}n.accessLink=l.link,n.getLinkToken=d,o()}}a(sl,"requireGetLinkToken");function jn(e,t){let n=e.getLinkUrlBytes;return(typeof n=="number"?n:Buffer.byteLength(e.originalUrl||e.url||"","utf8"))>wn?(z(t),t.status(414).json({error:`Request URL exceeds ${wn} bytes`,code:"url_too_long",hint:"Use chunked drafts: GET \u2026/draft/new then \u2026/draft/<id>/append?seq=&data= then \u2026/draft/<id>/submit?sha256=, or use POST /mcp/api/v1/proposals with Authorization: Bearer <token>.",limitBytes:wn}),!0):!1}a(jn,"rejectOversizedGetUrl");function j(e,t,n,r,o,s){z(e),e.status(t).json({error:n,code:r,...o?{hint:o}:{},...s})}a(j,"jsonError");function il(){return(e,t)=>{Pi(e),z(t),t.status(404).json({error:"Not found",code:"not_found"})}}a(il,"getLinkNotFoundHandler");var jh="link_create_not_supported";function al(){return(e,t)=>{Pi(e),z(t),t.status(403).json({error:"GET access links cannot create tokens",code:jh,hint:"Ask an admin with * permission to mint tokens via POST /mcp/api/v1/links on the /ai-access dashboard (Access tokens tab). This get_link URL cannot create, list, or revoke tokens."})}}a(al,"getLinkMintNotSupportedHandler");function H(e,t){let n=e[t];if(typeof n=="string"&&n.length>0)return n;if(Array.isArray(n)&&typeof n[0]=="string"&&n[0].length>0)return n[0]}a(H,"queryString");function Ue(e){switch(e){case"draft_not_found":return"Start a new draft with GET \u2026/draft/new.";case"draft_already_submitted":return"This draft already created a proposal; poll that proposal or start a new draft.";case"chunk_conflict":return"Re-send the same data for that seq, or start a new draft.";case"payload_too_large":return"Shrink the content; totalBytes counts decoded bytes against maxBytes.";case"invalid_seq":return"seq must be a non-negative integer (0, 1, 2, \u2026).";case"invalid_chunk":case"invalid_chunk_data":return"data must be base64url of the raw chunk bytes (A-Z, a-z, 0-9, -, _).";case"missing_chunks":return"Upload every seq from 0 through max before submit.";case"invalid_sha256":return"sha256 must be exactly 64 hexadecimal characters.";case"draft_hash_mismatch":return"sha256 must be the hex digest of the assembled UTF-8 bytes (before base64url).";case"draft_empty":return"Append at least one chunk before submit.";case"node_not_found":return"Call GET \u2026/flows/<id> and use a node id from that response (or use mode=json with op:add).";case"field_not_found":return"Use a property that exists on the node, or mode=json to add/replace nodes.";case"missing_draft_params":return"Example: \u2026/draft/new?flow=tab1&rev=abc&node=n1&field=template";case"version_mismatch":return"Re-read the flow and start a new draft with the current rev.";case"protected_property":return"Omit id, type, and z from set props. wires are allowed when targets exist in the flow.";case"invalid_wires":return"wires must list existing node ids (or add those nodes in the same patch).";default:return}}a(Ue,"draftErrorHint");async function Uh(e,t,n){let r;try{r=await e.getFlow(t)}catch(s){let i=W(s);if(i)return{code:i.body.code,message:i.body.error};throw s}let o=[...Array.isArray(r.flow.nodes)?r.flow.nodes:[],...Array.isArray(r.flow.configs)?r.flow.configs:[]];for(let s of n){let i=o.find(d=>d.id===s.nodeId);if(!i)return{code:"node_not_found",message:`Node '${s.nodeId}' was not found in flow '${t}'`};if(!(s.field in i))return{code:"field_not_found",message:`Field '${s.field}' was not found on node '${s.nodeId}'`}}return null}a(Uh,"validateFieldDraftTargets");function Bh(e,t,n,r){if(r)try{let o=Ar(r);if(!Array.isArray(o)||o.length===0)throw new Error("targets must be a non-empty array");return{ok:!0,mode:"field",targets:o.map(i=>{if(!i||typeof i!="object")throw new Error("bad target");let d=i,l=typeof d.nodeId=="string"?d.nodeId:typeof d.node=="string"?d.node:"",c=typeof d.field=="string"?d.field:"";if(!l||!c)throw new Error("bad target");return{nodeId:l,field:c}})}}catch{return{ok:!1,status:400,error:"targets must be base64url JSON array of {nodeId,field}",code:"invalid_targets"}}if(n==="json"||!e&&!t)return{ok:!0,mode:"json"};if(!e||!t){let o=Ue("missing_draft_params");return{ok:!1,status:400,error:"node and field are required for field drafts (or pass mode=json)",code:"missing_draft_params",...o?{hint:o}:{}}}return{ok:!0,mode:"field"}}a(Bh,"planDraftMode");async function qh(e,t,n,r){try{let o=await Uh(t,n,r);if(o){let s=o.code==="node_not_found"||o.code==="field_not_found"?404:400;return j(e,s,o.message,o.code,Ue(o.code)),!0}}catch(o){let s=W(o);return s?(j(e,s.statusCode,s.body.error,s.body.code,Ue(s.body.code)),!0):(B(e,o),!0)}return!1}a(qh,"rejectInvalidFieldTargets");function ll(e,t){return async(n,r)=>{if(jn(n,r))return;z(r);let o=H(n.query,"flow"),s=H(n.query,"rev"),i=H(n.query,"node"),d=H(n.query,"field"),l=H(n.query,"summary"),c=H(n.query,"title")??l,u=H(n.query,"mode"),p=H(n.query,"targets");if(!o||!s){j(r,400,"flow and rev query parameters are required","missing_draft_params",Ue("missing_draft_params"));return}if(!n.accessLink){j(r,401,"Access link required","access_link_missing");return}try{let h=await(await Sr(t,s)).getFlow(o);if(h.rev!==s){r.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:s,currentRev:h.rev,hint:Ue("version_mismatch")});return}}catch(h){let g=W(h);if(g){j(r,g.statusCode,g.body.error,g.body.code,g.body.code==="flow_not_found"?we("flow_not_found","get")??"Use an id from GET ./flows":Ue(g.body.code));return}B(r,h);return}let f=Bh(i,d,u,p);if(!f.ok){j(r,f.status,f.error,f.code,f.hint);return}if(f.mode==="field"){let h=f.targets&&f.targets.length>0?f.targets:i&&d?[{nodeId:i,field:d}]:[];if(h.length>0&&await qh(r,t,o,h))return}let m=e.create({linkId:n.accessLink.id,flowId:o,expectedRev:s,mode:f.mode,...i?{nodeId:i}:{},...d?{field:d}:{},...f.targets?{targets:f.targets}:{},...l?{summary:l}:{},...c?{title:c}:{}});r.status(201).json({ok:!0,draftId:m.id,mode:m.mode,expiresAt:new Date(m.expiresAtMs).toISOString(),maxBytes:K,hint:"Upload base64url chunks (~4\u20135 KB raw each; base64 adds ~33%; keep URL under ~8 KB) with GET \u2026/draft/<draftId>/append?seq=N&data=\u2026 then submit with sha256."})}}a(ll,"getLinkDraftNewHandler");function cl(e){return(t,n)=>{if(jn(t,n))return;z(n);let r=t.params.draftId,o=H(t.query,"seq"),s=H(t.query,"data");if(typeof r!="string"||!r){j(n,400,"Missing draft id","missing_draft_id");return}if(!t.accessLink){j(n,401,"Access link required","access_link_missing");return}if(o===void 0||s===void 0){j(n,400,"seq and data query parameters are required","missing_chunk_params","data must be base64url of the raw chunk bytes.");return}let i=Number(o);try{let d=e.append(r,t.accessLink.id,i,s);n.json({ok:!0,draftId:r,receivedSeqs:d.receivedSeqs,missingSeqs:d.missingSeqs,totalBytes:d.totalBytes,maxBytes:d.maxBytes,hint:d.missingSeqs.length===0?"No gaps so far; call submit when all chunks are uploaded":`Upload missing seqs: ${d.missingSeqs.join(",")}`})}catch(d){if(d instanceof Qt||d instanceof en||d instanceof G){j(n,d.statusCode,d.message,d.code,Ue(d.code),d instanceof en?{proposalId:d.proposalId}:void 0);return}B(n,d);return}}}a(cl,"getLinkDraftAppendHandler");function zh(e,t){let n=[];if(e.targets&&e.targets.length>0){let r;try{r=JSON.parse(t)}catch{let o=e.targets[0];if(e.targets.length===1&&o)return[{nodeId:o.nodeId,set:{[o.field]:t}}];throw new G("Multi-target draft body must be JSON","invalid_draft_body")}if(Array.isArray(r)){for(let o of r){let s=o;if(!s.nodeId||!s.field)throw new G("Invalid multi-target entry","invalid_draft_body");n.push({nodeId:s.nodeId,set:{[s.field]:s.value}})}return n}if(r&&typeof r=="object"){let o=r;for(let s of e.targets){let i=`${s.nodeId}.${s.field}`;if(!(i in o)&&!(s.field in o))throw new G(`Missing value for ${i} in draft JSON`,"invalid_draft_body");n.push({nodeId:s.nodeId,set:{[s.field]:i in o?o[i]:o[s.field]}})}return n}throw new G("Invalid multi-target draft JSON","invalid_draft_body")}if(e.nodeId&&e.field)return[{nodeId:e.nodeId,set:{[e.field]:t}}];throw new G("Field draft missing node/field","invalid_draft")}a(zh,"editsFromFieldDraft");function ul(e,t,n,r,o,s){return async(i,d)=>{if(jn(i,d))return;z(d);let l=i.params.draftId,c=H(i.query,"sha256"),u=H(i.query,"title");if(typeof l!="string"||!l){j(d,400,"Missing draft id","missing_draft_id");return}if(!i.accessLink){j(d,401,"Access link required","access_link_missing");return}if(!c){j(d,400,"sha256 query parameter is required","missing_sha256","Pass sha256 hex of the assembled UTF-8 bytes (before base64url).");return}try{let{draft:p,assembled:f}=e.assembleAndVerify(l,i.accessLink.id,c),m=p.flowId,h,g=p.expectedRev,y=await Sr(r,p.expectedRev);if(p.mode==="field"){let x=zh(p,f),C=await kr(y,p.flowId,x);if(C.expectedRev!==p.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:p.expectedRev,currentRev:C.expectedRev,draftDeleted:!1,hint:"Draft was kept. Re-read the flow and start a new draft with the current rev (or retry if you refresh rev)."});return}h=C.flow,g=C.expectedRev}else{let x=xr(JSON.parse(f),p.flowId);if(m=x.flowId,x.kind==="replace_flow")h={...x.flow,id:x.flow.id||m,type:x.flow.type??"tab",nodes:x.flow.nodes??[]};else{let C=await Gt(y,m,x.ops,{expectedRev:p.expectedRev,...s?{nodeTypes:s}:{}});if(C.expectedRev!==p.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:p.expectedRev,currentRev:C.expectedRev,draftDeleted:!1,hint:"Draft was kept. Re-read the flow and start a new draft with the current rev."});return}h=C.flow,g=C.expectedRev}}let w=u??p.title??p.summary,v=V(i,o),L=ge(i.query),P=await Tt(t,n,i.accessLink,{type:"replace_flow",expectedRev:g,payload:{flowId:m,flow:h},...w?{title:w}:{}},r,v,s);e.markSubmitted(l,i.accessLink.id,P.id);let I={...ie(P,{...v,include:L}),mine:!0},D=te(t),T=Qe(t,t.get(P.id),i.accessLink.id);d.status(201).json({ok:!0,proposal:I,sha256:(0,dl.createHash)("sha256").update(Buffer.from(f,"utf8")).digest("hex"),...et(T),...pe(D),hint:`Waiting for human approval. Tell the user about "${I.displayName}" (${I.reviewUrl}). Poll GET \u2026/proposals/<id> for status.`})}catch(p){if(p instanceof en){j(d,p.statusCode,p.message,p.code,Ue(p.code),{proposalId:p.proposalId});return}if(p instanceof Qt||p instanceof G){j(d,p.statusCode,p.message,p.code,Ue(p.code));return}if(p instanceof SyntaxError){j(d,400,"Assembled draft is not valid JSON","invalid_draft_body");return}let f=W(p);if(f){d.status(f.statusCode).json({...f.body,...Ue(f.body.code)?{hint:Ue(f.body.code)}:{}});return}B(d,p);return}}}a(ul,"getLinkDraftSubmitHandler");se();rt();Io();Wt();Oe();nt();Ro();var Wh={node_already_exists:"Choose a new node id, or use op:set / op:remove on the existing node.",protected_property:"Omit id, type, and z from set props. wires are allowed when targets exist in the flow.",invalid_wires:"wires must list existing node ids (or add those nodes in the same patch).",invalid_patch:"Pass patch as base64url JSON with nodes[{op,\u2026.}] or type/flow for replace_flow.",invalid_patch_json:"Pass patch as base64url JSON with nodes[{op,\u2026.}] or type/flow for replace_flow.",missing_edits:"Provide set.<prop>=value, find+replace+field, or patch nodes with at least one op.",missing_node_id:"Pass node=<id> with set.<prop>= or find/replace, or include id on each patch.nodes entry.",missing_flow_id:"Pass flow=<id> from GET \u2026/flows.",missing_field:"Pass field=<prop> (e.g. template, func) with find= and replace=.",missing_find:"Pass a non-empty find= string (URL-encoded).",invalid_occurrence:"occurrence must be a 1-based index within the match count.",invalid_replace:"Pass replace= as a string (URL-encoded; empty string is allowed to delete the find text).",conflicting_edits:"Use either find/replace/field or set.<prop>=, not both in one propose URL.",invalid_node_type:"node.type must be a string (not a number or object). Use the palette type name exactly.",invalid_node:"add op needs node as a JSON object: { id, type, \u2026 } \u2014 not a string or array.",z_mismatch:"Each node.z must match the flow id (tab id) you are editing."};function _i(e,t){return we(e,"get",{cause:t})??Wh[e]??Pr(e)}a(_i,"proposeErrorHint");function Gh(e,t,n){e.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:t,currentRev:n,hint:_i("version_mismatch")})}a(Gh,"sendVersionMismatch");async function Kh(e,t,n,r,o){let s=xr(Ar(t),n),i=s.flowId;if(s.kind==="replace_flow")return{ok:!0,builtFlowId:i,builtFlow:{...s.flow,id:s.flow.id||i,type:s.flow.type??"tab",nodes:s.flow.nodes??[]},expectedRev:r};let d=await Gt(e,i,s.ops,{expectedRev:r,...o?{nodeTypes:o}:{}});return d.expectedRev!==r?{ok:!1,kind:"version_mismatch",expectedRev:r,currentRev:d.expectedRev}:{ok:!0,builtFlowId:i,builtFlow:d.flow,expectedRev:d.expectedRev}}a(Kh,"buildFromPatch");async function Jh(e,t,n,r,o){let s=await kr(e,t,[{nodeId:n,set:r}]);return s.expectedRev!==o?{ok:!1,kind:"version_mismatch",expectedRev:o,currentRev:s.expectedRev}:{ok:!0,builtFlowId:t,builtFlow:s.flow,expectedRev:s.expectedRev}}a(Jh,"buildFromSetProps");async function Vh(e,t,n,r,o,s,i,d){let l=await Gt(e,t,[{op:"replace_text",nodeId:n,field:r,find:o,replace:s,...i!==void 0?{occurrence:i}:{}}]);return l.expectedRev!==d?{ok:!1,kind:"version_mismatch",expectedRev:d,currentRev:l.expectedRev}:{ok:!0,builtFlowId:t,builtFlow:l.flow,expectedRev:l.expectedRev}}a(Vh,"buildFromReplaceText");function Yh(e){let t=Object.prototype.hasOwnProperty.call(e.query,"replace");return{flowId:H(e.query,"flow"),rev:H(e.query,"rev"),nodeId:H(e.query,"node"),patchEncoded:H(e.query,"patch"),setProps:ci(e.query),findText:H(e.query,"find"),replaceText:t?H(e.query,"replace")??"":void 0,fieldName:H(e.query,"field"),occurrenceRaw:H(e.query,"occurrence")}}a(Yh,"readProposeQuery");function Be(e,t,n){j(e,400,n,t,_i(t))}a(Be,"jsonProposeError");function Xh(e,t){if(t===void 0||t==="")return{ok:!0};let n=Number(t);return Number.isFinite(n)?{ok:!0,occurrence:Math.trunc(n)}:(Be(e,"invalid_occurrence","occurrence must be a positive integer"),{ok:!1})}a(Xh,"parseOccurrenceQuery");async function Zh(e,t,n,r,o){if(n.patchEncoded)return Kh(t,n.patchEncoded,n.flowId,r,o);if(n.findText!==void 0||n.replaceText!==void 0||n.fieldName!==void 0){if(Object.keys(n.setProps).length>0)return Be(e,"conflicting_edits","Cannot combine replace_text (find/replace/field) with set.<prop> in one request"),null;if(!n.flowId)return Be(e,"missing_flow_id","flow query parameter is required"),null;if(!n.nodeId)return Be(e,"missing_node_id","node query parameter is required"),null;if(!n.fieldName)return Be(e,"missing_field","field query parameter is required for replace_text"),null;if(n.findText===void 0||n.findText.length===0)return Be(e,"missing_find","find query parameter must be a non-empty string"),null;if(n.replaceText===void 0)return Be(e,"invalid_replace","replace query parameter is required (may be empty)"),null;let i=Xh(e,n.occurrenceRaw);return i.ok?Vh(t,n.flowId,n.nodeId,n.fieldName,n.findText,n.replaceText,i.occurrence,r):null}return n.flowId?n.nodeId?Object.keys(n.setProps).length===0?(Be(e,"missing_edits","At least one set.<prop>=value is required"),null):Jh(t,n.flowId,n.nodeId,n.setProps,r):(Be(e,"missing_node_id","node query parameter is required (or pass patch=)"),null):(Be(e,"missing_flow_id","flow query parameter is required"),null)}a(Zh,"buildProposeFromQuery");function pl(e,t,n,r,o){return async(s,i)=>{if(jn(s,i))return;z(i);let d=Yh(s),l=H(s.query,"summary"),c=H(s.query,"title")??l;if(!d.rev){Be(i,"missing_expected_rev","rev query parameter is required");return}if(!s.accessLink){j(i,401,"Access link required","access_link_missing");return}try{let u=await Sr(n,d.rev),p=await Zh(i,u,d,d.rev,o);if(!p)return;if(!p.ok){Gh(i,p.expectedRev,p.currentRev);return}let f=V(s,r),m=ge(s.query),h=await Tt(e,t,s.accessLink,{type:"replace_flow",expectedRev:p.expectedRev,payload:{flowId:p.builtFlowId,flow:p.builtFlow},...c?{title:c}:{}},n,f,o),g={...ie(h,{...f,include:m}),mine:!0},y=te(e),w=Qe(e,e.get(h.id),s.accessLink.id),v=h.warnings;i.status(201).json({ok:!0,proposal:g,...l?{summary:l}:{},...et(w),...pe(y),...v&&v.length>0?{warnings:v}:{},hint:`Waiting for human approval. Tell the user about "${g.displayName}" (${g.reviewUrl}). Poll GET \u2026/proposals/<id> for status.${v&&v.length>0?` Soft warnings (${v.length}) are non-blocking \u2014 configure later if needed.`:""}`})}catch(u){let p=W(u,"get");if(p){let f=_i(p.body.code,p.body.cause);i.status(p.statusCode).json({...p.body,...f?{hint:f}:{}});return}B(i,u)}}}a(pl,"getLinkProposeHandler");se();rt();se();var Qh=50,fl=40,em=20;function tm(e){return Buffer.byteLength(JSON.stringify(e),"utf8")}a(tm,"nodeBytes");function nm(e){let t={id:e.id,type:e.type,bytes:tm(e)};return typeof e.name=="string"&&(t.name=e.name),"wires"in e&&(t.wires=e.wires),t}a(nm,"summarizeNode");async function Uo(e,t){let{rev:n,flow:r}=await He(e,t),o=r,s=Array.isArray(o.nodes)?o.nodes.map(nm):[],i={rev:n,flowId:o.id||t,nodes:s,nodeCount:s.length};return typeof o.label=="string"&&(i.label=o.label),typeof o.type=="string"&&(i.type=o.type),i}a(Uo,"getFlowSummary");function hl(e){let t=[];for(let n of e){if(!n||typeof n!="object"||Array.isArray(n))continue;let r=n;(r.type==="tab"||r.type==="subflow"||Array.isArray(r.nodes))&&t.push(r)}return t}a(hl,"collectTabs");async function ml(e,t,n){if(!t)throw new k("Missing node id","missing_node_id");if(n){let{rev:d,flow:l}=await He(e,n),c=l,u=Array.isArray(c.nodes)?c.nodes:[],p=Array.isArray(c.configs)?c.configs:[],f=[...u,...p].find(m=>m.id===t);if(!f)throw new k("Node was not found","node_not_found",{statusCode:404});return{rev:d,flowId:n,node:f}}let{rev:r,flows:o}=await pt(e),s,i=null;for(let d of o){if(!d||typeof d!="object"||Array.isArray(d))continue;let l=d;if(l.id===t&&typeof l.type=="string"&&l.type!=="tab"&&l.type!=="subflow"){s=l,i=typeof l.z=="string"&&l.z?l.z:null;break}}if(!s)for(let d of hl(o)){let l=d.id;if(l)try{let u=(await He(e,l)).flow,f=[...u.nodes??[],...u.configs??[]].find(m=>m.id===t);if(f){s=f,i=l;break}}catch{}}if(!s)throw new k("Node was not found","node_not_found",{statusCode:404});return{rev:r,flowId:i,node:s}}a(ml,"findNodeInFlows");async function Bo(e,t,n){let r=await ml(e,t,n);return{rev:r.rev,flowId:r.flowId,node:r.node}}a(Bo,"getNodeById");function rm(e){if(e===void 0||e==="")return;let t=/^(\d+)(?:-(\d+))?$/.exec(e.trim());if(!t)throw new k("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");let n=Number(t[1]),r=t[2]!==void 0?Number(t[2]):n;if(!Number.isInteger(n)||!Number.isInteger(r)||n<1)throw new k("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");if(r<n)throw new k("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");return{start:n,end:r}}a(rm,"parseLineRange");async function qo(e,t,n,r={}){if(!n)throw new k("Missing field name","missing_field");let o=await ml(e,t,r.flowId);if(!(n in o.node))throw new k("Field was not found on the node","field_not_found");let s=o.node[n];if(typeof s!="string")throw new k("Field is not a string","field_not_string");let i=s.split(`
985
+ `),d=i.length,l=Buffer.byteLength(s,"utf8"),c=rm(r.lines);if(!c)return{rev:o.rev,flowId:o.flowId,nodeId:t,field:n,value:s,totalLines:d,bytes:l};if(c.start>d)throw new k(`lines range is out of bounds (start ${c.start} > ${d} total lines)`,"line_range_out_of_bounds",{details:{totalLines:d}});let u=c.start-1,p=Math.min(c.end,i.length),f=i.slice(u,p).join(`
986
+ `);return{rev:o.rev,flowId:o.flowId,nodeId:t,field:n,value:f,totalLines:d,bytes:l,lineRange:{start:c.start,end:Math.min(c.end,d)}}}a(qo,"getNodeField");function om(e){let t=[];for(let[n,r]of Object.entries(e))n==="id"||n==="type"||n==="z"||n==="wires"||typeof r=="string"&&r.length>0&&t.push({field:n,value:r});return t}a(om,"stringFields");function sm(e,t,n){let r=Math.max(0,t-fl),o=Math.min(e.length,t+n+fl),s=e.slice(r,o).replace(/\s+/g," ");return r>0&&(s=`\u2026${s}`),o<e.length&&(s=`${s}\u2026`),s}a(sm,"snippetAround");function im(e,t){let n=1;for(let r=0;r<t&&r<e.length;r+=1)e.charCodeAt(r)===10&&(n+=1);return n}a(im,"lineNumberAtIndex");function Ci(e,t,n,r,o){for(let{field:s,value:i}of om(e)){if(r.length>=o)return!0;let d=[],l=new Set,c=-1,u=a((f,m)=>{l.has(f)||(l.add(f),d.push(f)),c<0&&(c=m)},"pushLine");if(!n.includes(`
987
987
  `)){let f=i.split(`
988
- `);for(let h=0;h<f.length;h+=1){let g=(f[h]??"").indexOf(n);if(g>=0){let y=0;for(let A=0;A<h;A+=1)y+=(f[A]?.length??0)+1;u(h+1,y+g)}}}if(n.includes(`
989
- `)){let f=0;for(;f<=i.length;){let h=i.indexOf(n,f);if(h<0)break;u(tm(i,h),h),f=h+Math.max(1,n.length)}}if(d.length===0)continue;let p=d.length;r.push({flowId:t,nodeId:e.id,field:s,lines:d.slice(0,Vh),lineCount:p,snippet:em(i,Math.max(0,c),n.length)})}return r.length>=o}a(Ai,"searchInNode");function ul(e){return[...e.nodes??[],...e.configs??[]]}a(ul,"nodePool");async function nm(e,t,n,r,o){let s=await Me(e,t);for(let i of ul(s.flow))if(Ai(i,t,n,r,o))return{rev:s.rev,truncated:!0};return{rev:s.rev,truncated:!1}}a(nm,"searchOneFlow");async function rm(e,t,n,r){let o=await ut(e),s=new Set;for(let i of ll(o.flows))if(i.id)try{let d=await Me(e,i.id);for(let l of ul(d.flow))if(s.add(l.id),Ai(l,i.id,t,n,r))return{rev:o.rev,truncated:!0}}catch{}for(let i of o.flows){if(!i||typeof i!="object"||Array.isArray(i))continue;let d=i;if(!d.id||s.has(d.id)||d.type==="tab"||d.type==="subflow")continue;let l=typeof d.z=="string"?d.z:null;if(Ai(d,l,t,n,r))return{rev:o.rev,truncated:!0}}return{rev:o.rev,truncated:!1}}a(rm,"searchAllFlows");async function Uo(e,t,n={}){if(typeof t!="string"||!t.trim())throw new b("q query parameter is required","missing_query");let r=typeof n.cap=="number"&&n.cap>0?Math.floor(n.cap):Jh,o=[],s=n.flowId?await nm(e,n.flowId,t,o,r):await rm(e,t,o,r),i={rev:s.rev,q:t,hits:o,hitCount:o.length,truncated:s.truncated,cap:r};return n.flowId&&(i.flowId=n.flowId),i}a(Uo,"searchFlows");function pl(e){if(e===void 0)return!1;let t=e.toLowerCase();return t==="1"||t==="true"||t==="yes"}a(pl,"isTruthyQueryFlag");Pe();function om(e,t,n){switch(e){case"node_not_found":case"flow_not_found":case"field_not_found":return n??we(e,t);default:return we(e,t)}}a(om,"layeredReadErrorHint");function tn(e){return nt[e]??"Request failed"}a(tn,"safeErrorText");function _e(e,t,n){e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("Content-Type","application/json; charset=utf-8"),e.status(t).json(n)}a(_e,"sendJson");function Bo(e,t,n,r){let o=W(t,n);if(!o)return!1;let s=o.body.code,i={error:tn(s),code:s};typeof o.body.matchCount=="number"&&(i.matchCount=o.body.matchCount),typeof o.body.totalLines=="number"&&(i.totalLines=o.body.totalLines),typeof o.body.expectedRev=="string"&&(i.expectedRev=o.body.expectedRev),typeof o.body.currentRev=="string"&&(i.currentRev=o.body.currentRev);let d=om(s,n,r);return d&&(i.hint=d),_e(e,o.statusCode,i),!0}a(Bo,"sendMappedError");function qo(e){_e(e,502,{error:tn("upstream_error"),code:"upstream_error"})}a(qo,"sendUpstreamError");function zo(e,t="rest"){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){_e(r,400,{error:tn("missing_flow_id"),code:"missing_flow_id"});return}let s=pl(M(n.query,"summary"));try{if(s){_e(r,200,await Do(Xe(n,e),o));return}_e(r,200,await Me(Xe(n,e),o))}catch(i){if(Bo(r,i,t))return;qo(r)}}}a(zo,"getFlowOrSummaryHandler");function Wo(e,t="rest"){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||!o){_e(r,400,{error:tn("missing_node_id"),code:"missing_node_id"});return}let s=M(n.query,"flow");try{_e(r,200,await Ho(Xe(n,e),o,s))}catch(i){if(Bo(r,i,t,t==="get"?"Call GET \u2026/flows/<id>?summary=1 or \u2026/search?q=\u2026":t==="mcp"?void 0:"Call GET \u2026/flows/<id>?summary=1 or \u2026/search?q=\u2026"))return;qo(r)}}}a(Wo,"getNodeHandler");function Go(e,t="rest"){return async(n,r)=>{let o=n.params.id,s=n.params.name;if(typeof o!="string"||!o){_e(r,400,{error:tn("missing_node_id"),code:"missing_node_id"});return}if(typeof s!="string"||!s){_e(r,400,{error:tn("missing_field"),code:"missing_field"});return}let i=M(n.query,"flow"),d=M(n.query,"lines");try{_e(r,200,await jo(Xe(n,e),o,s,{...i?{flowId:i}:{},...d?{lines:d}:{}}))}catch(l){if(Bo(r,l,t,"Use summary/search to discover node ids and fields."))return;qo(r)}}}a(Go,"getNodeFieldHandler");function Ko(e,t="rest"){return async(n,r)=>{let o=M(n.query,"q");if(!o){_e(r,400,{error:tn("missing_query"),code:"missing_query",hint:t==="get"?"Example: GET \u2026/search?q=TODO&flow=tab1":we("missing_query",t)});return}let s=M(n.query,"flow");try{_e(r,200,await Uo(Xe(n,e),o,s?{flowId:s}:{}))}catch(i){if(Bo(r,i,t))return;qo(r)}}}a(Ko,"searchFlowsHandler");ie();qt();var sm={missing_node_type:"Missing node type",missing_query:"q query parameter is required",node_type_not_found:"Node type was not found",catalogue_unavailable:"Node-RED catalogue unavailable",unknown_node_type:"Unknown node type",upstream_error:"Upstream error"};function Rr(e){return sm[e]??"Request failed"}a(Rr,"safeErrorText");function Ct(e,t,n){e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("Content-Type","application/json; charset=utf-8"),e.setHeader("Cache-Control","no-store"),e.status(t).json(n)}a(Ct,"sendJson");function Si(e,t){let n=W(t);if(!n)return!1;let r=n.body.code,o={error:Rr(r),code:r};return typeof n.body.type=="string"&&(o.type=n.body.type),Array.isArray(n.body.nearest)&&(o.nearest=n.body.nearest),typeof n.body.didYouMean=="string"?(o.didYouMean=n.body.didYouMean,o.caseSensitive=!0,o.hint=`Types are case-sensitive \u2014 use '${n.body.didYouMean}' exactly.`):(r==="node_type_not_found"||r==="unknown_node_type")&&(o.hint="Call GET \u2026/node-types?q= or \u2026/palette/search?q=. Ask the user to install via Manage palette, then recheck node-types. This API never installs."),r==="catalogue_unavailable"&&(o.hint="Retry later; palette search is recommend-only and never installs packages."),Ct(e,n.statusCode,o),!0}a(Si,"sendMappedError");function Jo(e){return async(t,n)=>{try{let r=M(t.query,"q"),o=await ao(e.nodeTypes,r?{q:r}:{});Ct(n,200,o)}catch(r){if(Si(n,r))return;Ct(n,502,{error:Rr("upstream_error"),code:"upstream_error"})}}}a(Jo,"listNodeTypesHandler");function Vo(e){return async(t,n)=>{try{let r=t.params.type,o=typeof r=="string"?decodeURIComponent(r):"",s=M(t.query,"help"),i=!(s==="0"||s==="false"||s==="no"||s==="omit"),d=await Bt(e.nodeTypes,o,{includeHelp:i});Ct(n,200,d)}catch(r){if(Si(n,r))return;Ct(n,502,{error:Rr("upstream_error"),code:"upstream_error"})}}}a(Vo,"getNodeTypeHandler");function Yo(e){return async(t,n)=>{try{let r=M(t.query,"q");if(!r){Ct(n,400,{error:Rr("missing_query"),code:"missing_query",hint:"Pass ?q= to search the Node-RED catalogue (recommend only)."});return}let o=await lo(e.nodeTypes,r,{...e.catalogueFetcher?{catalogueFetcher:e.catalogueFetcher}:{},...e.catalogueUrl?{catalogueUrl:e.catalogueUrl}:{}});Ct(n,200,o)}catch(r){if(Si(n,r))return;Ct(n,502,{error:Rr("upstream_error"),code:"upstream_error"})}}}a(Yo,"searchPaletteHandler");zt();Te();Je();function On(e,t){return e[t]??""}a(On,"lineAt");function Pr(e,t,n){let r=e[t];return r?r[n]??0:0}a(Pr,"cell");function fl(e,t,n,r){let o=e[t];o&&(o[n]=r)}a(fl,"setCell");function ml(e,t){let n=hl(e),r=hl(t),o=n.length,s=r.length,i=[];for(let u=0;u<=o;u+=1){let p=[];for(let f=0;f<=s;f+=1)p.push(0);i.push(p)}for(let u=o-1;u>=0;u-=1)for(let p=s-1;p>=0;p-=1)On(n,u)===On(r,p)?fl(i,u,p,Pr(i,u+1,p+1)+1):fl(i,u,p,Math.max(Pr(i,u+1,p),Pr(i,u,p+1)));let d=[],l=0,c=0;for(;l<o&&c<s;){let u=On(n,l),p=On(r,c);u===p?(d.push({op:"equal",text:u}),l+=1,c+=1):Pr(i,l+1,c)>=Pr(i,l,c+1)?(d.push({op:"remove",text:u}),l+=1):(d.push({op:"add",text:p}),c+=1)}for(;l<o;)d.push({op:"remove",text:On(n,l)}),l+=1;for(;c<s;)d.push({op:"add",text:On(r,c)}),c+=1;return d}a(ml,"diffLines");function hl(e){return e.length===0?[]:e.split(`
990
- `)}a(hl,"splitLines");Fe();yr();et();Pe();var yl=new Set(["func","template","info","payload","code","text","format","syntax"]),im=new Set(["x","y","w","h","wires","g"]),am=new Set(["w","h","g"]);function nn(e){if(typeof e.name=="string"&&e.name.length>0)return e.name;if(typeof e.label=="string"&&e.label.length>0)return e.label}a(nn,"nodeName");function gl(e){let t=new Map;for(let n of e.nodes??[])t.set(n.id,n);for(let n of e.configs??[])t.set(n.id,n);return t}a(gl,"collectNodes");function es(e){if(e===void 0)return"undefined";if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(es).join(",")}]`;let t=e;return`{${Object.keys(t).sort().map(r=>`${JSON.stringify(r)}:${es(t[r])}`).join(",")}}`}a(es,"stableStringify");function wl(e,t){return es(e)===es(t)}a(wl,"valuesEqual");function ts(e){return typeof e=="string"?e:null}a(ts,"asText");function dm(e){return/<\/?[a-z][\s\S]*>/i.test(e.slice(0,800))}a(dm,"looksLikeHtml");function lm(e,t,n){if(t!=="template")return!1;let r=typeof e.format=="string"?e.format.toLowerCase():"";return r==="html"||r==="handlebars"||r==="mustache"?!0:dm(n)}a(lm,"isHtmlTemplateField");function bl(e,t,n){let r=ts(n);if(yl.has(t)&&r!==null){let o={key:t,kind:"text",value:r};return lm(e,t,r)&&(o.preview="html"),o}return{key:t,kind:"scalar",value:n===void 0?null:n}}a(bl,"toPresenceProperty");function cm(e,t){let n=[];for(let r of[...Object.keys(e)].sort())if(!(am.has(r)||r==="id"||r==="z")){if(r==="wires"){let o=vl(e,t);n.push({key:"wires",kind:"scalar",value:o.length>0?o:e.wires??null});continue}n.push(bl(e,r,e[r]))}return n}a(cm,"nodeProperties");function vl(e,t){let n=e.wires;if(!Array.isArray(n)||n.length===0)return[];let r=[];return n.forEach((o,s)=>{if(!Array.isArray(o))return;let i=[];for(let d of o){if(typeof d!="string"||!d)continue;let l=t.get(d);i.push({id:d,name:l?nn(l)??l.type:d})}r.push({port:s,to:i})}),r}a(vl,"describeWiresStructured");var um=new Set(["func","template","code","text"]);function pm(e,t){let n=[],r=nn(e);r&&n.push({key:"name",kind:"scalar",value:r});let o=vl(e,t);o.length>0&&n.push({key:"wires",kind:"scalar",value:o});for(let s of um){if(!(s in e))continue;let i=ts(e[s]);i===null||i.length===0||n.push(bl(e,s,i))}return n}a(pm,"buildKeyFields");function fm(e,t,n){let r=wt(e.type),o=nn(e),s=o?` "${o}"`:"",i=t?` in "${t}"`:"",d=n===1?"1 field":`${n} fields`;return`Changes ${r} node${s}${i} (${d})`}a(fm,"buildModifiedSummary");function hm(e,t,n){let r=wt(e.type),o=nn(e),s=t==="added"?"Adds":"Removes",i=t==="added"?"to":"from",d=o?` ${JSON.stringify(o)}`:"",l=n?` ${i} flow ${JSON.stringify(n)}`:"",c=`${s} ${r} node${d}${l}`;if(t==="added"&&r==="template"){let u=typeof e.field=="string"&&e.field?e.field:"";u&&(c+=`, output to msg.${u}`)}return c}a(hm,"buildPresenceSummary");function Xo(e,t,n,r){let o={id:e.id,type:e.type,summary:hm(e,t,n),keyFields:pm(e,r),properties:cm(e,r)},s=nn(e);return s!==void 0&&(o.name=s),o}a(Xo,"toPresenceNode");function mm(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let o of[...n].sort()){if(im.has(o)||o==="id"||o==="z")continue;let s=e[o],i=t[o];if(wl(s,i))continue;let d=ts(s),l=ts(i);if(yl.has(o)&&(d!==null||l!==null)){let c=d??"",u=l??"";r.push({key:o,kind:"text",before:c,after:u,lines:ml(c,u)});continue}r.push({key:o,kind:"scalar",before:s===void 0?null:s,after:i===void 0?null:i})}return r}a(mm,"compareNodes");function gm(e,t){let n=["label","disabled","info","env"],r=[];for(let o of n)wl(e[o],t[o])||r.push(o);return r}a(gm,"compareTabMeta");function Zo(e){return typeof e.label=="string"&&e.label.trim()?e.label.trim():""}a(Zo,"flowLabelOf");function Qo(e,t){let n=gl(t),r=gl(e),o=Zo(e)||Zo(t),s=Zo(t)||Zo(e),i=[],d=[],l=[];for(let c of e.nodes??[])n.has(c.id)||i.push(Xo(c,"added",o,r));for(let c of e.configs??[])n.has(c.id)||i.push(Xo(c,"added",o,r));for(let c of t.nodes??[])r.has(c.id)||d.push(Xo(c,"removed",s,n));for(let c of t.configs??[])r.has(c.id)||d.push(Xo(c,"removed",s,n));for(let[c,u]of r){let p=n.get(c);if(!p)continue;let f=mm(p,u);if(f.length>0){let h={id:c,type:u.type||p.type,summary:fm(u,o||s,f.length),changes:f},m=nn(u)??nn(p);m!==void 0&&(h.name=m),l.push(h)}}return l.sort((c,u)=>c.id.localeCompare(u.id)),{summary:{added:i.length,removed:d.length,modified:l.length,tabChanges:gm(t,e),beforeNodeCount:n.size,afterNodeCount:r.size},added:i,removed:d,modified:l}}a(Qo,"buildDiffSections");function xi(e,t,n={}){let r=Qo(e.payload.flow,t.flow),o=n.currentRev??t.rev,s=e.expectedRev,i=n.baseRevStale??(U(e.status)&&s!==o),d={proposalId:e.id,flowId:e.payload.flowId,expectedRev:s,currentRev:o,baseRevStale:i,compareAgainst:n.compareAgainst??"current",...r};return e.warnings&&e.warnings.length>0&&(d.warnings=e.warnings.map(l=>({...l}))),n.baseSnapshotMissing&&(d.baseSnapshotMissing=!0),n.vsCurrent&&(d.vsCurrent=n.vsCurrent),d}a(xi,"buildProposalDiff");async function $n(e,t,n=e){let r=e.payload.flowId,o={id:r,type:"tab",nodes:[],configs:[]},s=e.expectedRev,i=o;try{let u=await t.getFlow(r);s=u.rev,i=u.flow}catch(u){if(!(u instanceof X))throw u;try{s=(await t.getFlows()).rev}catch{}}let d=U(e.status),l=d&&e.expectedRev!==s,c=e.baseFlow;if(d){let u=Oe(t),p=await u.getFlows(),f=oi(n,p),h=u!==t,m=h?lt(n):n,g=Wt(p.flows,r,n.baseFlow)??o,y=h?bt(g).value:g,A=xi(m,{rev:e.expectedRev,flow:m.baseFlow??y},{currentRev:p.rev,compareAgainst:m.baseFlow?"base":"current",baseSnapshotMissing:m.baseFlow===void 0,baseRevStale:e.expectedRev!==p.rev});return A.rebase={state:f.state,conflicts:h?Xs(f.conflicts):f.conflicts,...f.mergedHash?{mergedHash:f.mergedHash}:{}},A.liveChanges=Qo(y,m.baseFlow??y),f.state!=="conflict"&&f.merged&&(A.vsCurrent=Qo(h?bt(f.merged).value:f.merged,y)),A}if(c!==void 0){let u=l?Qo(e.payload.flow,i):void 0;return xi(e,{rev:e.expectedRev,flow:c},{currentRev:s,compareAgainst:"base",baseRevStale:l,...u?{vsCurrent:u}:{}})}return xi(e,{rev:s,flow:i},{compareAgainst:"current",baseSnapshotMissing:!0,baseRevStale:l})}a($n,"computeProposalDiff");Fe();et();Be();ke();var kl=require("path");function ns(e){return(0,kl.join)(e.userDir,".ai-access")}a(ns,"getStorageDir");var ym=["status:read","flows:read","proposals:create","proposals:read-own"];function Ti(){return En()}a(Ti,"readPackageVersion");function wm(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/1e3)):0}a(wm,"secondsRemainingUntil");function bm(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/6e4)):0}a(bm,"minutesRemainingUntil");function vm(e,t){let n=Se(t),r=ht(t),o=`${n}/openapi.yaml`,s=`${n}/openapi.json`;return e==="rest"?{api:n,openapi:o,openapiJson:s,discovery:n}:e==="mcp"?{rpc:r,discovery:n}:{discovery:n}}a(vm,"baseUrlsForTokenKind");function Mn(e,t){let n=Gr(e,t),r=Ee(e.settings);if(t.auth){let o=t.auth.extra?.user;return{ok:!0,version:Ti(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:t.auth.scopes.includes("propose")?"propose":"read",capabilities:{view:!0,manageLinks:!1,approveProposals:!1},defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,maxActiveLinks:r.maxActiveLinks,defaultDeploymentType:r.defaultDeploymentType,caller:{kind:"oauth",clientId:t.auth.clientId,user:typeof o=="string"?o:"owner",scopes:t.auth.scopes}}}if(t.accessLink){let o=Date.now();return{ok:!0,version:Ti(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:null,capabilities:{view:!0,manageLinks:!1,approveProposals:!1},defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,maxActiveLinks:r.maxActiveLinks,defaultDeploymentType:r.defaultDeploymentType,caller:{kind:"access-link",linkId:t.accessLink.id,tokenKind:t.accessLink.kind,...t.accessLink.label!==void 0?{label:t.accessLink.label}:{},expiresAt:t.accessLink.expiresAt,minutesRemaining:bm(t.accessLink.expiresAt,o),secondsRemaining:wm(t.accessLink.expiresAt,o),permissions:[...ym],baseUrls:vm(t.accessLink.kind,e.settings.httpAdminRoot)}}}return{ok:!0,version:Ti(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:n.permission,capabilities:n.capabilities,storageDir:ns(e.settings),defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,maxActiveLinks:r.maxActiveLinks,defaultDeploymentType:r.defaultDeploymentType,caller:{kind:"admin",...n.permission?{permission:n.permission}:{}}}}a(Mn,"buildStatus");function Al(e,t){let n=t.draftStore??new Fo,r=t.rateLimiters??Zd(t.rateLimit??e.settings.aiAccess),o=e.httpAdmin,s="/mcp/a/:token",i=[r.ip,el(e,t.linkStore),r.token];o.use("/mcp/a",Hd()),o.get(s,...i,O((u,p)=>{z(p),p.setHeader("X-Content-Type-Options","nosniff");let f=Se(e.settings.httpAdminRoot),h=u.accessLink;if(!h){j(p,401,"Access link required","access_link_missing");return}let m=Gd({link:h,openapiPath:`${f}/openapi.yaml`,restApiBase:f,mcpRpcPath:ht(e.settings.httpAdminRoot),publicOrigin:vs(u)});p.status(200).type("text/markdown; charset=utf-8").send(m)})),o.get(`${s}/status`,...i,O((u,p)=>{z(p),p.json(Mn(e,u))})),o.get(`${s}/flows`,...i,O(async(u,p)=>{z(p);try{p.json(await ut(t.flowAdapter))}catch(f){let h=W(f);if(h){p.status(h.statusCode).json({...h.body,hint:"Retry after the owner fixes the plugin runtime."});return}j(p,502,f instanceof Error?f.message:String(f),"upstream_error")}})),o.get(`${s}/flows/:id`,...i,O(async(u,p)=>{z(p),await zo(t.flowAdapter,"get")(u,p)})),o.get(`${s}/nodes/:id`,...i,O(async(u,p)=>{z(p),await Wo(t.flowAdapter,"get")(u,p)})),o.get(`${s}/nodes/:id/field/:name`,...i,O(async(u,p)=>{z(p),await Go(t.flowAdapter,"get")(u,p)})),o.get(`${s}/search`,...i,O(async(u,p)=>{z(p),await Ko(t.flowAdapter,"get")(u,p)}));let d={nodeTypes:t.nodeTypesAdapter,...t.catalogueFetcher?{catalogueFetcher:t.catalogueFetcher}:{},...t.catalogueUrl?{catalogueUrl:t.catalogueUrl}:{}};o.get(`${s}/node-types`,...i,O(async(u,p)=>{z(p),await Jo(d)(u,p)})),o.get(`${s}/node-types/:type`,...i,O(async(u,p)=>{z(p),await Vo(d)(u,p)})),o.get(`${s}/palette/search`,...i,O(async(u,p)=>{z(p),await Yo(d)(u,p)}));let l=e.settings.httpAdminRoot;km(o,s,i,t,l),o.get(`${s}/propose`,...i,O(al(t.proposalStore,t.auditLog,t.flowAdapter,l,t.nodeTypesAdapter))),xm(o,s,i,n,t,l);let c=nl();return o.all(`${s}/links`,c),o.all(`${s}/links/:id`,c),o.use("/mcp/a",tl()),n}a(Al,"registerGetLinkRoutes");function km(e,t,n,r,o){e.get(`${t}/proposals`,...n,O(async(s,i)=>{z(i);let d=s.accessLink;if(!d){j(i,401,"Access link required","access_link_missing");return}let l=V(s,o),c=ge(s.query),u=xo(s.query),p=await Qe(r.flowAdapter),f=p?$e(r.proposalStore,p).expired:ne(r.proposalStore),h=Tn(r.proposalStore,d,l,u);i.json({proposals:h.proposals.map(m=>he(r.proposalStore,d,m,{...l,include:c},{includeConflicts:!1})),...Jt({...h,returned:h.proposals.length}),...ue(f)})})),e.get(`${t}/proposals/:id`,...n,O(async(s,i)=>{z(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){j(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){j(i,401,"Access link required","access_link_missing");return}try{let c=V(s,o),u=ge(s.query),p=await Qe(r.flowAdapter),f=ct(r.proposalStore,l,d,c);i.json(he(r.proposalStore,l,f,{...c,include:u},{...p?{live:p}:{}}))}catch(c){let u=W(c,"get");if(u){i.status(u.statusCode).json({...u.body,hint:u.body.hint??"List proposals with GET \u2026/proposals."});return}B(i,c);return}})),Am(e,t,n,r,o),Sm(e,t,n,r,o)}a(km,"registerGetLinkProposalRoutes");function Am(e,t,n,r,o){e.get(`${t}/proposals/:id/withdraw`,...n,O((s,i)=>{z(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){j(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){j(i,401,"Access link required","access_link_missing");return}let c=s.query.confirm;if(c!=="1"&&c!=="true"){j(i,400,"Withdraw over GET requires confirm=1","confirm_required",we("confirm_required","get"));return}let u=typeof s.query.reason=="string"?s.query.reason:void 0,p=V(s,o),f=ge(s.query);try{let h=Rn(r.proposalStore,r.auditLog,l,d,u,p);i.json(he(r.proposalStore,l,h,{...p,include:f}))}catch(h){let m=W(h,"get");if(m){let g=m.body.code;j(i,m.statusCode,nt[g]??"Request failed",g,we(g,"get"));return}j(i,500,"Internal server error","internal_error");return}}))}a(Am,"registerGetLinkWithdrawRoute");function Sm(e,t,n,r,o){e.get(`${t}/proposals/:id/diff`,...n,O(async(s,i)=>{z(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){j(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){j(i,401,"Access link required","access_link_missing");return}try{let c=ct(r.proposalStore,l,d,V(s,o)),u=M(s.query,"format"),p=In(u==="json"||u==="md"?void 0:u);if(!p.ok){j(i,400,"Invalid diff format","invalid_diff_format","Valid formats: summary, full (default summary).");return}i.json(Ln(await $n(c,r.flowAdapter,r.proposalStore.get(d)),p.format))}catch(c){let u=W(c);if(u){i.status(u.statusCode).json(u.body);return}B(i,c);return}}))}a(Sm,"registerGetLinkDiffRoute");function xm(e,t,n,r,o,s){e.get(`${t}/draft/new`,...n,O(ol(r,o.flowAdapter))),e.get(`${t}/draft/:draftId/append`,...n,O(sl(r))),e.get(`${t}/draft/:draftId/submit`,...n,O(il(r,o.proposalStore,o.auditLog,o.flowAdapter,s,o.nodeTypesAdapter)))}a(xm,"registerGetLinkDraftRoutes");se();var Sl=require("module");function Tm(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Sl.createRequire)(e)}a(Tm,"createPluginRequire");function Rm(e){try{let r=Tm()("@node-red/runtime")._?.adminApi?.httpAdmin;if(r&&typeof r.use=="function"&&r!==e.httpAdmin)return r}catch{}return null}a(Rm,"resolveEditorHttpAdmin");function xl(e){let t=[e.httpAdmin],n=Rm(e);return n&&t.push(n),t}a(xl,"httpAdminAppsForMcpErrors");var Tl=Symbol.for("com55.ai-access.mcpJsonErrorHandler");function Pm(e){let t=e??"/";for(;t.endsWith("/");)t=t.slice(0,-1);let n=new Set;for(let r of t?["",t]:[""])for(let o of["/mcp","/mcp/rpc"])n.add(r+o),n.add(`${r+o}/`);return r=>n.has(r)}a(Pm,"mcpRpcMatcher");function Ri(e){return e.includes("/mcp")}a(Ri,"isMcpPath");function _m(e){return e.path||e.url||e.originalUrl||""}a(_m,"requestPath");function Em(e){if(!e||typeof e!="object")return!1;let t=e;if(t.type==="entity.parse.failed"||e instanceof SyntaxError)return!0;let n=typeof e.message=="string"?e.message:"";return/JSON/i.test(n)&&(t.status===400||t.statusCode===400)}a(Em,"isJsonParseError");function Cm(e){if(!e||typeof e!="object")return!1;let t=e;return t.type==="entity.too.large"||t.status===413||t.statusCode===413||t.message==="request entity too large"}a(Cm,"isPayloadTooLarge");function Im(e){let t=Pm(e);return(n,r,o,s)=>{if(o.headersSent){s(n);return}let i=_m(r);if(Em(n)&&Ri(i)){if(t(i)){o.status(400).json({jsonrpc:"2.0",error:{code:-32700,message:"Parse error"},id:null});return}o.status(400).json({error:"Invalid JSON body",code:"invalid_json"});return}if(Cm(n)&&Ri(i)){o.status(413).json({error:`Request body exceeds limit of ${K} bytes`,code:"payload_too_large"});return}if(Ri(i)){if(t(i)){o.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:No(n).error,data:{code:"internal_error"}},id:null});return}o.status(500).json(No(n));return}s(n)}}a(Im,"mcpJsonErrorHandler");function Lm(e,t){let n=e;n[Tl]||(n[Tl]=!0,e.use(Im(t)))}a(Lm,"attachOnce");function Rl(e){for(let t of xl(e))Lm(t,e.settings.httpAdminRoot)}a(Rl,"attachMcpJsonErrorHandlers");xr();var rn=class extends Error{static{a(this,"AccessLinkLimitError")}code="access_link_limit";statusCode=409;constructor(t){super(`Cannot create access link: at most ${t} active links are allowed. Revoke an existing link first.`),this.name="AccessLinkLimitError"}},on=class extends Error{static{a(this,"AccessLinkNotFoundError")}code="access_link_not_found";statusCode=404;constructor(t){super(`Access link '${t}' was not found`),this.name="AccessLinkNotFoundError"}},_r=class extends Error{static{a(this,"AccessLinkValidationError")}code="access_link_invalid";statusCode=400;constructor(t){super(t),this.name="AccessLinkValidationError"}},It=class extends Error{static{a(this,"AccessLinkTtlError")}code="invalid_ttl";statusCode=400;hint=Vt;constructor(t){super(t),this.name="AccessLinkTtlError"}};var sn=require("crypto");xr();function Pi(e){return e==="get_link"||e==="rest"||e==="mcp"}a(Pi,"isAccessLinkKind");function _i(e){return e??"get_link"}a(_i,"resolveAccessLinkKind");function Pl(e){return(0,sn.createHash)("sha256").update(e,"utf8").digest("hex")}a(Pl,"hashToken");function Nm(e,t){let n=Buffer.from(e,"utf8"),r=Buffer.from(t,"utf8");return n.length!==r.length?!1:(0,sn.timingSafeEqual)(n,r)}a(Nm,"hashesEqual");function Er(e){let t={id:e.id,kind:_i(e.kind),createdAt:new Date(e.createdAtMs).toISOString(),expiresAt:new Date(e.expiresAtMs).toISOString()};return e.label!==void 0&&(t.label=e.label),e.lastUsedAtMs!==void 0&&(t.lastUsedAt=new Date(e.lastUsedAtMs).toISOString()),e.createdBy!==void 0&&(t.createdBy=e.createdBy),t}a(Er,"toPublic");var an=class{static{a(this,"AccessLinkStore")}links=new Map;defaultTtlSeconds;maxActiveLinks;now;randomId;randomToken;constructor(t={}){this.defaultTtlSeconds=t.defaultTtlSeconds??14400,this.maxActiveLinks=t.maxActiveLinks??10,this.now=t.now??(()=>Date.now()),this.randomId=t.randomId??(()=>(0,sn.randomBytes)(12).toString("hex")),this.randomToken=t.randomToken??(()=>`aia_${(0,sn.randomBytes)(32).toString("base64url")}`)}configureDefaults(t){typeof t.defaultTtlSeconds=="number"&&Number.isFinite(t.defaultTtlSeconds)&&t.defaultTtlSeconds>0&&(this.defaultTtlSeconds=Math.floor(t.defaultTtlSeconds)),typeof t.maxActiveLinks=="number"&&Number.isFinite(t.maxActiveLinks)&&t.maxActiveLinks>0&&(this.maxActiveLinks=Math.floor(t.maxActiveLinks))}isActive(t,n){return t.revokedAtMs!==void 0?!1:t.expiresAtMs>n}list(){let t=this.now();return[...this.links.values()].filter(n=>this.isActive(n,t)).sort((n,r)=>r.createdAtMs-n.createdAtMs).map(Er)}create(t={}){let n=this.now();if([...this.links.values()].filter(p=>this.isActive(p,n)).length>=this.maxActiveLinks)throw new rn(this.maxActiveLinks);let o=this.defaultTtlSeconds;if(t.ttlSeconds!==void 0){if(typeof t.ttlSeconds!="number"||!Number.isFinite(t.ttlSeconds)||t.ttlSeconds<=0)throw new It("ttlSeconds must be a positive number");o=Math.floor(t.ttlSeconds)}let s=typeof t.label=="string"&&t.label.trim().length>0?t.label.trim():void 0,i=_i(t.kind),d=ui(i);if(o>d)throw new It(pi(i,d));let l=this.randomId(),c=this.randomToken(),u={id:l,tokenHash:Pl(c),kind:i,createdAtMs:n,expiresAtMs:n+o*1e3};return s!==void 0&&(u.label=s),t.createdBy!==void 0&&(u.createdBy=t.createdBy),this.links.set(l,u),{...Er(u),token:c}}revoke(t){let n=this.links.get(t);if(!n)throw new on(t);return n.revokedAtMs??=this.now(),Er(n)}getById(t){if(typeof t!="string"||t.length===0)return null;let n=this.links.get(t);return n?Er(n):null}validateResult(t){if(typeof t!="string"||t.length===0)return{status:"invalid"};let n=Pl(t),r=this.now();for(let o of this.links.values())if(Nm(o.tokenHash,n))return o.revokedAtMs!==void 0?{status:"invalid"}:o.expiresAtMs<=r?{status:"expired",expiresAt:new Date(o.expiresAtMs).toISOString()}:(o.lastUsedAtMs=r,{status:"active",link:Er(o)});return{status:"invalid"}}validate(t){let n=this.validateResult(t);return n.status==="active"?n.link:null}size(){return this.links.size}clear(){this.links.clear()}upsertRecordForTest(t){let n={...t};delete n.token,this.links.set(t.id,n)}};xr();ve();ke();function Ei(e,t){return t}a(Ei,"storeForRequest");function El(e){let t=typeof e.user?.username=="string"?e.user.username:void 0;return t?{kind:"admin",username:t}:{kind:"admin"}}a(El,"adminActor");function rs(e){let t=Ee(e.settings);return new an({defaultTtlSeconds:t.defaultLinkTtlSeconds,maxActiveLinks:t.maxActiveLinks})}a(rs,"createConfiguredLinkStore");function Cl(e){e.setHeader("Cache-Control","no-store"),e.setHeader("Pragma","no-cache")}a(Cl,"noStore");function Il(e,t){return(n,r)=>{let s=Ei(e,t).list();Cl(r),r.json({links:s})}}a(Il,"listLinksHandler");function Ll(e,t,n){return(r,o)=>{let s=r.body??{},i={};if(s.ttlSeconds!==void 0){if(typeof s.ttlSeconds!="number"){o.status(400).json({error:"ttlSeconds must be a positive number",code:"invalid_ttl",hint:Vt});return}i.ttlSeconds=s.ttlSeconds}if(typeof s.label=="string"&&(i.label=s.label),s.kind!==void 0){if(!Pi(s.kind)){o.status(400).json({error:"kind must be one of: 'get_link', 'rest', 'mcp'",code:"invalid_link_kind"});return}i.kind=s.kind}typeof r.user?.username=="string"&&(i.createdBy=r.user.username);try{let d=Ei(e,t).create(i),l=d.kind;if(n){let c=Date.parse(d.createdAt),u=Date.parse(d.expiresAt),p=Number.isFinite(c)&&Number.isFinite(u)&&u>c?Math.round((u-c)/1e3):void 0;n.append("link.created",El(r),{linkId:d.id,label:d.label,expiresAt:d.expiresAt,createdAt:d.createdAt,kind:l,...p!==void 0?{ttlSeconds:p}:{}})}if(Cl(o),l==="get_link"){let c=pa(r,e.settings.httpAdminRoot,d.token);o.status(201).json({...d,url:c});return}o.status(201).json({...d})}catch(d){if(d instanceof rn){o.status(d.statusCode).json({error:d.message,code:d.code});return}if(d instanceof It){o.status(d.statusCode).json({error:d.message,code:d.code,hint:d.hint});return}if(d instanceof _r){o.status(d.statusCode).json({error:d.message,code:d.code});return}B(o,d);return}}}a(Ll,"createLinkHandler");function Nl(e,t,n){return(r,o)=>{let s=r.params.id;if(typeof s!="string"||s.length===0){o.status(400).json({error:"Missing link id"});return}try{let i=Ei(e,t).revoke(s);if(n){let d=Date.parse(i.createdAt),l=Date.parse(i.expiresAt),c=Number.isFinite(d)&&Number.isFinite(l)&&l>d?Math.round((l-d)/1e3):void 0;n.append("link.revoked",El(r),{linkId:i.id,label:i.label,expiresAt:i.expiresAt,createdAt:i.createdAt,kind:i.kind,...c!==void 0?{ttlSeconds:c}:{}})}o.json({revoked:!0,link:i})}catch(i){if(i instanceof on){o.status(i.statusCode).json({error:i.message,code:i.code});return}B(o,i);return}}}a(Nl,"revokeLinkHandler");function Fm(e){let t=Date.parse(e.createdAt),n=Date.parse(e.expiresAt);if(Number.isFinite(t)&&Number.isFinite(n)&&n>t)return Math.round((n-t)/1e3)}a(Fm,"ttlSecondsFromLink");function Om(e,t){if(!t||e.action!=="link.created"&&e.action!=="link.revoked")return e;let n={...e.details??{}},r=typeof n.linkId=="string"?n.linkId:"";if(!r)return e;let o=n.label==null||n.label==="",s=n.expiresAt==null||n.expiresAt==="",i=n.createdAt==null||n.createdAt==="",d=n.ttlSeconds==null;if(!o&&!s&&!i&&!d)return e;let l=t.getById(r);if(!l)return e;if(o&&l.label&&(n.label=l.label),s&&(n.expiresAt=l.expiresAt),i&&(n.createdAt=l.createdAt),d){let c=Fm(l);c!==void 0&&(n.ttlSeconds=c)}return{...e,details:n}}a(Om,"enrichAuditLogEntry");function Fl(e,t){return(n,r)=>{let o=n.query.limit,s=100;if(typeof o=="string"&&o.length>0){let d=Number(o);Number.isFinite(d)&&d>0&&(s=Math.min(Math.floor(d),500))}let i=e.list(s).map(d=>Om(d,t));r.json({logs:i})}}a(Fl,"listLogsHandler");var Ii=require("@modelcontextprotocol/sdk/server/mcp.js"),Bl=require("@modelcontextprotocol/sdk/server/streamableHttp.js"),P=require("zod");Je();se();$t();ie();tt();qt();zt();Fe();et();var Ol=require("async_hooks");Pe();var $m="ai.redacted",$l=new Ol.AsyncLocalStorage;function Ml(e,t,n,r){if(!(r<=0))try{e.append($m,t,{redactions:r,operation:n})}catch{}}a(Ml,"appendRedactionAudit");function Dl(e){let t=$l.getStore();if(!t)return;let n=wo(e);n>0&&t.record(n)}a(Dl,"noteResponseRedactions");function Hl(e,t,n,r){return $l.run({record:a(o=>Ml(e,t,n,o),"record")},r)}a(Hl,"runWithRedactionAudit");function jl(e){let t=Array.isArray(e)?e[0]:e;if(t&&typeof t=="object"){let n=t;if(n.method==="tools/call"&&typeof n.params?.name=="string")return`mcp:${n.params.name.replace(/[^A-Za-z0-9_.-]/g,"").slice(0,64)}`;if(typeof n.method=="string")return`mcp:${n.method.slice(0,64)}`}return"mcp"}a(jl,"mcpOperationName");function Ci(e){let t=e.accessLink;if(t)return{kind:"access-link",linkId:t.id,...t.label!==void 0?{label:t.label}:{}}}a(Ci,"accessLinkAuditActor");function Ul(e){return(t,n,r)=>{if(!t.path.startsWith("/mcp/")||t.path.startsWith("/mcp/rpc")){r();return}let o=n.json.bind(n);n.json=(s=>{let i=Ci(t);if(i&&n.statusCode<400){let d;try{d=String(JSON.stringify(s))}catch{d=""}let l=t.route?.path,c=`${t.method} ${typeof l=="string"?l:"unknown"}`;Ml(e,i,c,wo(d))}return o(s)}),r()}}a(Ul,"redactionAuditMiddleware");Pe();function ql(){return"0.2.0-beta.7"}a(ql,"pluginVersion");function ae(e){let t=JSON.stringify(e);return Dl(t),{content:[{type:"text",text:t}]}}a(ae,"jsonResult");function Li(e){return e===!0?new Set(["payload","baseFlow"]):new Set}a(Li,"includeSetFromPayloadFlag");function de(e,t=500){let n=Sr(e,"mcp");if(n)return{isError:!0,content:[{type:"text",text:JSON.stringify({...n.body,statusCode:n.statusCode})}]};let r=e instanceof Error?e.message:String(e);return{isError:!0,content:[{type:"text",text:JSON.stringify({error:r,code:"internal_error",statusCode:t})}]}}a(de,"jsonError");var Mm=new Set(["list_proposals","list_my_proposals"]),Dm=new Set(["status","limit","offset","mine","flowId"]);function Hm(e){return!!e&&typeof e=="object"&&typeof e.safeParseAsync=="function"}a(Hm,"isZodLikeSchema");function jm(e,t,n){let r=t&&typeof t=="object"?t:{},o=n[0],s=o?.path&&o.path.length>0?String(o.path[0]):"(arguments)";if(Mm.has(e)&&Dm.has(s))return xn(s,r[s]);let i=n.map(l=>({parameter:l.path&&l.path.length>0?l.path.map(String).join("."):"(arguments)",message:l.message??"Invalid value"})),d=i.length===1?`Invalid argument '${i[0]?.parameter}' for tool ${e}: ${i[0]?.message}`:`${i.length} invalid arguments for tool ${e}`;return new b(d,"invalid_arguments",{details:{parameter:s,errors:i,hint:`Fix '${s}' to match the ${e} input schema (see tools/list).`}})}a(jm,"mcpArgumentError");function zl(e){let t=e,n=t.validateToolInput;typeof n=="function"&&(t.validateToolInput=async(r,o,s)=>{let i=r.inputSchema;if(!Hm(i))return n.call(e,r,o,s);let d=await i.safeParseAsync(o??{});if(d.success)return d.data;let l=Sr(jm(s,o,d.error.issues),"mcp"),c=l?{...l.body,statusCode:l.statusCode}:{};throw new Error(JSON.stringify(c))})}a(zl,"installMcpArgumentErrorMapping");function Wl(e,t,n,r){e.registerTool("get_status",{description:'Return plugin status (version, AI feature flags, storage path, deploy defaults). For access-link callers, permission is null and caller.kind is "access-link" \u2014 that is expected, not an error. Use before other tools to confirm the plugin is enabled.'},()=>{try{return ae(Mn(t,r))}catch(o){return de(o)}}),e.registerTool("list_flows",{description:'List all flows as a flat Admin API v2 array plus the current rev string. Likely secrets are shown as "[REDACTED]" (nodes list redacted paths in _aiRedactions). Returns { rev, flows }. Call this (or get_flow) before submit_proposal so expectedRev matches.'},async()=>{try{return ae(await ut(n.flowAdapter))}catch(o){return de(o,502)}}),e.registerTool("get_flow",{description:"Get one tab/subflow by id with nested nodes/configs and the current rev. Pass summary=true for a lightweight node list (id/type/name/wires/bytes, no field contents). Prefer summary \u2192 search_flows \u2192 get_node_field(lines) \u2192 replace_text \u2192 submit_proposal. Returns { rev, flow } or summary shape. Use rev as expectedRev when submitting.",inputSchema:{id:P.z.string().min(1).describe("Flow / tab id"),summary:P.z.boolean().optional().describe("When true, return node list with sizes only (no field contents)")}},async({id:o,summary:s})=>{try{return ae(s?await Do(n.flowAdapter,o):await Me(n.flowAdapter,o))}catch(i){return de(i,502)}}),e.registerTool("get_node",{description:"Get one full node by id (includes owning flowId when known). Optional flow scopes the lookup.",inputSchema:{id:P.z.string().min(1).describe("Node id"),flow:P.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,flow:s})=>{try{return ae(await Ho(n.flowAdapter,o,s))}catch(i){return de(i,502)}}),e.registerTool("get_node_field",{description:'Get one string field from a node. Optional lines="A-B" (1-based inclusive) returns a slice; response always includes totalLines and bytes.',inputSchema:{id:P.z.string().min(1).describe("Node id"),field:P.z.string().min(1).describe("Field name (e.g. template, func)"),lines:P.z.string().optional().describe("Optional 1-based line range A or A-B"),flow:P.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,field:s,lines:i,flow:d})=>{try{return ae(await jo(n.flowAdapter,o,s,{...d?{flowId:d}:{},...i?{lines:i}:{}}))}catch(l){return de(l,502)}}),e.registerTool("search_flows",{description:"Text search across string fields. Returns hits with node id, field, line numbers, and a short snippet. Results are capped; truncated=true when the cap is hit. Optional flow scopes to one tab.",inputSchema:{q:P.z.string().describe("Substring to find (case-sensitive; non-empty required)"),flow:P.z.string().min(1).optional().describe("Optional tab id")}},async({q:o,flow:s})=>{try{return ae(await Uo(n.flowAdapter,o,s?{flowId:s}:{}))}catch(i){return de(i,502)}}),e.registerTool("list_node_types",{description:"List installed Node-RED palette node types (type, module, category, description, \u2026). Optional q filters type/module/category/description (case-insensitive). Results are capped. Call this before proposing nodes with unfamiliar types.",inputSchema:{q:P.z.string().optional().describe("Optional case-insensitive filter")}},async({q:o})=>{try{return ae(await ao(n.nodeTypesAdapter,o?{q:o}:{}))}catch(s){return de(s,502)}}),e.registerTool("get_node_type",{description:"Get detail for one installed node type: defaults (JSON), inputs/outputs (always set), requiredConfigNodeTypes, help sections (summary/inputs/outputs), category/module/color. Types are case-sensitive. Pass includeHelp=false to omit help. 404 node_type_not_found when unknown (didYouMean on case mismatch; else nearest names).",inputSchema:{type:P.z.string().min(1).describe("Node type id (e.g. inject, template) \u2014 case-sensitive"),includeHelp:P.z.boolean().optional().describe("When false, omit help sections (default true)")}},async({type:o,includeHelp:s})=>{try{return ae(await Bt(n.nodeTypesAdapter,o,{includeHelp:s!==!1}))}catch(i){return de(i,502)}}),e.registerTool("search_palette",{description:"Search the public Node-RED catalogue for modules to recommend. Returns module id, description, version, updated, link, and installed boolean (cross-checked against installed modules). Core node-red types are not in the catalogue \u2014 use list_node_types. RECOMMEND ONLY \u2014 never installs.",inputSchema:{q:P.z.string().describe("Search string (id / description / keywords; non-empty required)")}},async({q:o})=>{try{return ae(await lo(n.nodeTypesAdapter,o,{...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{}}))}catch(s){return de(s,502)}})}a(Wl,"registerReadTools");function Gl(e,t,n,r,o){let s=V(r,t.settings.httpAdminRoot);e.registerTool("submit_proposal",{description:`Submit a replace_flow proposal for owner approval (does not deploy). Preferred for small string edits: payload: { flowId, nodes: [{ op:"replace_text", node, field, find, replace, occurrence? }] } \u2014 find must be unique unless occurrence (1-based) is set; multiple replace_text ops apply in order; yields a short readable diff. Or send a full tab: payload.flow from get_flow (id, type, label?, nodes[], configs?). Inputs (all top-level, next to each other): type="replace_flow", expectedRev (required string from list_flows/get_flow rev; expected_rev alias accepted), optional title (\u226480 chars), and payload: { flowId, flow? } and/or payload.nodes / top-level nodes. Do not nest expectedRev inside payload. Secret values appear as "[REDACTED]" in reads; leave the placeholder unchanged to keep the stored secret (it is restored on submit), or replace it with a new value to change it. Payload size limit is ${K} bytes (413 if exceeded). Recent known expectedRev snapshots are accepted and auto-rebased; unknown revisions return 409 version_mismatch. Open proposals auto-rebase on revision changes; conflict blocks approval until resolved. Proposals expire after the configured TTL (default 7 days). replace_text errors: 400 text_not_found; 409 ambiguous_match with matchCount. Unknown palette types return 400 unknown_node_type with nearest matches \u2014 call list_node_types / search_palette and ask the user to install via Manage palette (never auto-install). Returns a lean proposal (id, status, title, storedTitle, flowLabel, displayName, reviewUrl, changeSummary, \u2026) by default; title may gain " in <flow>"; storedTitle is the title as saved (your title without that suffix, or the auto-generated title, which may already name the flow). Pass include_payload=true to include payload and baseFlow. When telling the user about the proposal, cite displayName and reviewUrl \u2014 not the raw id. Humans approve on the /ai-access dashboard (reviewUrl opens/scrolls to it).`,inputSchema:{type:P.z.literal("replace_flow").describe("Must be 'replace_flow'"),expectedRev:P.z.string().min(1).optional().describe("Top-level current flow revision from get_flow/list_flows (required at submit; sit next to type and payload, not inside payload)"),expected_rev:P.z.string().min(1).optional().describe("Alias for expectedRev (snake_case); prefer expectedRev"),title:P.z.string().max(80).optional().describe("Optional short human title (\u226480 chars). Auto-generated from changes when omitted."),include_payload:P.z.boolean().optional().describe("When true, include payload and baseFlow in the response (default lean)"),nodes:P.z.array(P.z.record(P.z.string(),P.z.unknown())).optional().describe("Optional node ops (set/add/remove/replace_text). Same as payload.nodes when flow is omitted."),payload:P.z.object({flowId:P.z.string().min(1).describe("Tab id to replace"),flow:P.z.object({id:P.z.string().optional(),type:P.z.string().optional().describe("Usually 'tab' (included by get_flow)"),label:P.z.string().optional(),nodes:P.z.array(P.z.record(P.z.string(),P.z.unknown())).optional().describe("Child nodes for the tab (from get_flow.nodes)"),configs:P.z.array(P.z.record(P.z.string(),P.z.unknown())).optional()}).passthrough().optional().describe("Full tab from get_flow; omit when using nodes ops"),nodes:P.z.array(P.z.record(P.z.string(),P.z.unknown())).optional().describe("Node ops applied to the live tab (prefer replace_text for small string edits)")}).describe("Replacement flow payload and/or node ops")}},async i=>{try{let d=await Tt(n.proposalStore,n.auditLog,o,{...i,expectedRev:typeof i.expectedRev=="string"&&i.expectedRev.length>0?i.expectedRev:i.expected_rev},n.flowAdapter,s,n.nodeTypesAdapter),l={...fe(d,{...s,include:Li(i.include_payload)}),mine:!0},c=ne(n.proposalStore),u=Ye(Ve(n.proposalStore,n.proposalStore.get(d.id),o.id));return ae({...l,...u,...ue(c)})}catch(d){return de(d)}}),Bm(e,n,o,s),qm(e,n,o,s)}a(Gl,"registerProposalTools");function Um(e,t,n,r){let o={mine:P.z.boolean().optional().describe("true: only proposals created by this access link; false: only other links\u2019 proposals"),status:P.z.union([P.z.string(),P.z.array(P.z.string())]).optional().describe("Filter by proposal status: one value, an array, or a comma-separated string (pending, conflict, approved, rejected, failed, withdrawn, expired, superseded). An empty array or empty string means no status filter."),flowId:P.z.string().optional().describe("Filter by flow tab id (payload.flowId)"),limit:P.z.number().int().min(1).max(Sn).optional().describe(`Page size (1\u2013${Sn}). Omit to return all matches.`),offset:P.z.number().int().min(0).optional().describe("Skip this many filtered results (default 0)"),include_payload:P.z.boolean().optional().describe("When true, include payload and baseFlow on each proposal")},s=a(async i=>{try{let d=Li(i.include_payload),l=await Qe(t.flowAdapter),c=l?$e(t.proposalStore,l).expired:ne(t.proposalStore),u={};typeof i.mine=="boolean"&&(u.mine=i.mine);let p=ci(i.status);p&&(u.status=p),typeof i.flowId=="string"&&i.flowId.length>0&&(u.flowId=i.flowId),typeof i.limit=="number"&&(u.limit=i.limit),typeof i.offset=="number"&&(u.offset=i.offset);let f=Tn(t.proposalStore,n,r,u);return ae({proposals:f.proposals.map(h=>he(t.proposalStore,n,h,{...r,include:d},{includeConflicts:!1})),...Jt({...f,returned:f.proposals.length}),...ue(c)})}catch(d){return de(d)}},"listProposalsHandler");e.registerTool("list_proposals",{description:"List proposals visible to this access link (all proposals by default). Each item includes mine:true|false for the caller. Filter with mine, status, and/or flowId; paginate with limit/offset (response includes total, offset, hasMore, nextOffset). Invalid filters return invalid_filter. Returns lean { proposals, total, \u2026 } with title, displayName, reviewUrl, changeSummary (no payload/baseFlow unless include_payload=true). Prefer displayName + reviewUrl when referring to a proposal with the user (not the raw id).",inputSchema:o},s),e.registerTool("list_my_proposals",{description:"Alias of list_proposals. Prefer list_proposals. Lists proposals visible to this access link with optional mine/status/flowId filters and limit/offset pagination.",inputSchema:o},s)}a(Um,"registerListProposalsTools");function Bm(e,t,n,r){Um(e,t,n,r),e.registerTool("get_proposal",{description:"Get any proposal by id (all tokens can read). Response includes mine:true|false and, for open proposals (pending or conflict), conflictingProposals when other open work touches the same flow. Lean by default (no payload/baseFlow); pass include_payload=true for full bodies. Cite displayName + reviewUrl when talking to the user.",inputSchema:{id:P.z.string().min(1).describe("Proposal id (prop-\u2026)"),include_payload:P.z.boolean().optional().describe("When true, include payload and baseFlow")}},async({id:o,include_payload:s})=>{try{let i=await Qe(t.flowAdapter),d=ct(t.proposalStore,n,o,r);return ae(he(t.proposalStore,n,d,{...r,include:Li(s)},{...i?{live:i}:{}}))}catch(i){return de(i)}})}a(Bm,"registerProposalQueryTools");function qm(e,t,n,r){e.registerTool("get_proposal_diff",{description:"Diff any proposal against its stored baseFlow snapshot (what changed). Optional format=summary|full (default summary). Open proposals include rebase and liveChanges; conflict blocks approval, otherwise vsCurrent shows the merged deployment.",inputSchema:{id:P.z.string().min(1).describe("Proposal id (prop-\u2026)"),format:P.z.enum(["summary","full"]).optional().describe("Diff payload format (default summary)")}},async({id:o,format:s})=>{try{let i=ct(t.proposalStore,n,o),d=In(s??"summary");if(!d.ok)return de(new b("Invalid diff format","invalid_diff_format",{details:{hint:"Valid formats: summary, full (default summary)."}}));let l=Ln(await $n(i,t.flowAdapter,t.proposalStore.get(o)),d.format);return ae(l)}catch(i){return de(i)}}),e.registerTool("resolve_proposal_conflict",{description:"Replace your own open proposal with a resolution based on expectedRev. Revision changes auto-rebase; conflict blocks approval until resolved. Open proposals expire after the configured TTL (default 7 days). Read the current flow first; payload accepts a full flow or nodes ops as in submit_proposal.",inputSchema:{id:P.z.string().min(1),expectedRev:P.z.string().min(1),title:P.z.string().max(80).optional(),payload:P.z.object({flowId:P.z.string().min(1),flow:P.z.record(P.z.string(),P.z.unknown()).optional(),nodes:P.z.array(P.z.record(P.z.string(),P.z.unknown())).optional()})}},async({id:o,...s})=>{try{let i=await To(t.proposalStore,t.auditLog,n,o,s,t.flowAdapter,r,t.nodeTypesAdapter);return ae(he(t.proposalStore,n,i,r))}catch(i){return de(i)}}),e.registerTool("withdraw_proposal",{description:"Withdraw one of YOUR open (pending or conflict) proposals (status becomes withdrawn, not rejected). Optional reason. Only the creating access link can withdraw; other tokens get 403. Cite displayName + reviewUrl when telling the user.",inputSchema:{id:P.z.string().min(1).describe("Proposal id (prop-\u2026) owned by this access link"),reason:P.z.string().optional().describe("Optional withdraw reason")}},({id:o,reason:s})=>{try{let i=Rn(t.proposalStore,t.auditLog,n,o,s,r);return ae(he(t.proposalStore,n,i,r))}catch(i){return de(i)}})}a(qm,"registerProposalActionTools");function zm(e,t,n){let r=new Ii.McpServer({name:"node-red-ai-access",version:ql()});zl(r);let o=n.accessLink;if(!o)throw new Error("Access link required");return Wl(r,e,t,n),Gl(r,e,t,n,o),r}a(zm,"createAiMcpServer");function Wm(e){let t=Ci(e);if(t)return t;let n=e.auth?.clientId??"",r=typeof e.auth?.extra?.user=="string"?e.auth.extra.user:"owner";return{kind:"access-link",linkId:to(n,r),label:"oauth"}}a(Wm,"mcpAuditActor");function Ni(e,t){return async(n,r,o)=>{try{if(!n.accessLink&&!n.auth){r.setHeader("WWW-Authenticate",ce),r.status(401).json({error:xe,code:qe});return}let s={...t,flowAdapter:kt(t.flowAdapter)},i=n.accessLink?zm(e,s,n):Gm(e,s,n),d=new Bl.StreamableHTTPServerTransport({enableJsonResponse:!0});await i.connect(d),await Hl(t.auditLog,Wm(n),jl(n.body),()=>d.handleRequest(n,r,n.body)),r.on("close",()=>{d.close(),i.close()})}catch(s){if(!r.headersSent){B(r,s);return}o(s instanceof Error?s:new Error(String(s)))}}}a(Ni,"mcpRpcPostHandler");function Gm(e,t,n){let r=new Ii.McpServer({name:"node-red-ai-access",version:ql()});if(zl(r),Wl(r,e,t,n),(n.auth?.scopes??[]).includes("propose")){let s=typeof n.auth?.extra?.user=="string"?n.auth.extra.user:"owner",i=n.auth?.clientId??"",l={id:to(i,s),kind:"mcp",createdAt:new Date().toISOString(),expiresAt:new Date((n.auth?.expiresAt??0)*1e3).toISOString(),createdBy:s,oauthClientId:i,oauthUser:s};Gl(r,e,t,n,l)}return r}a(Gm,"createOAuthMcpServer");function Cr(e,t){t.status(405).set("Allow","POST").json({jsonrpc:"2.0",error:{code:-32e3,message:"Method not allowed. Use POST for Streamable HTTP."},id:null})}a(Cr,"mcpRpcMethodNotAllowed");var Ir=require("crypto"),Hn=require("fs"),Mi=require("path"),Q=require("@modelcontextprotocol/sdk/server/auth/errors.js"),Xl=require("@modelcontextprotocol/sdk/server/auth/handlers/authorize.js"),Di=require("@modelcontextprotocol/sdk/server/auth/handlers/metadata.js"),Zl=require("@modelcontextprotocol/sdk/server/auth/handlers/register.js"),Ql=require("@modelcontextprotocol/sdk/server/auth/handlers/revoke.js"),ec=require("@modelcontextprotocol/sdk/server/auth/handlers/token.js"),tc=require("@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js"),nc=require("@modelcontextprotocol/sdk/server/auth/router.js"),rc=zn(require("express"),1);se();Be();function rt(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t]??t)}a(rt,"esc");var Kl={read:'<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5c-5 0-9 4.5-10 7 1 2.5 5 7 10 7s9-4.5 10-7c-1-2.5-5-7-10-7Zm0 11.5a4.5 4.5 0 1 1 0-9 4.5 4.5 0 0 1 0 9Zm0-2.3a2.2 2.2 0 1 0 0-4.4 2.2 2.2 0 0 0 0 4.4Z"/></svg>',propose:'<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 17.2V20h2.8l8.3-8.3-2.8-2.8L4 17.2Zm13.7-7.6a1 1 0 0 0 0-1.4l-1.9-1.9a1 1 0 0 0-1.4 0l-1.4 1.4 2.8 2.8 1.9-1.9Z"/></svg>'},Km={read:["Read your flows","View flows, nodes, node types and AI Access logs."],propose:["Propose flow changes","Submit change proposals. Nothing is deployed until you approve it in Node-RED."]},Fi='<svg class="logo" viewBox="0 0 48 48" aria-hidden="true"><rect width="48" height="48" rx="11" fill="#8f0000"/><path d="M11 15h9a4 4 0 0 1 4 4v10a4 4 0 0 0 4 4h9" fill="none" stroke="#fff" stroke-width="3" stroke-linecap="round"/><rect x="6" y="11" width="9" height="8" rx="2.5" fill="#fff"/><rect x="33" y="29" width="9" height="8" rx="2.5" fill="#fff"/><circle cx="37.5" cy="16" r="5" fill="none" stroke="#fff" stroke-width="2.4"/><path d="M37.5 13.5v5M35 16h5" stroke="#fff" stroke-width="2.2" stroke-linecap="round"/></svg>',Jm=`
988
+ `);for(let m=0;m<f.length;m+=1){let g=(f[m]??"").indexOf(n);if(g>=0){let y=0;for(let w=0;w<m;w+=1)y+=(f[w]?.length??0)+1;u(m+1,y+g)}}}if(n.includes(`
989
+ `)){let f=0;for(;f<=i.length;){let m=i.indexOf(n,f);if(m<0)break;u(im(i,m),m),f=m+Math.max(1,n.length)}}if(d.length===0)continue;let p=d.length;r.push({flowId:t,nodeId:e.id,field:s,lines:d.slice(0,em),lineCount:p,snippet:sm(i,Math.max(0,c),n.length)})}return r.length>=o}a(Ci,"searchInNode");function gl(e){return[...e.nodes??[],...e.configs??[]]}a(gl,"nodePool");async function am(e,t,n,r,o){let s=await He(e,t);for(let i of gl(s.flow))if(Ci(i,t,n,r,o))return{rev:s.rev,truncated:!0};return{rev:s.rev,truncated:!1}}a(am,"searchOneFlow");async function dm(e,t,n,r){let o=await pt(e),s=new Set;for(let i of hl(o.flows))if(i.id)try{let d=await He(e,i.id);for(let l of gl(d.flow))if(s.add(l.id),Ci(l,i.id,t,n,r))return{rev:o.rev,truncated:!0}}catch{}for(let i of o.flows){if(!i||typeof i!="object"||Array.isArray(i))continue;let d=i;if(!d.id||s.has(d.id)||d.type==="tab"||d.type==="subflow")continue;let l=typeof d.z=="string"?d.z:null;if(Ci(d,l,t,n,r))return{rev:o.rev,truncated:!0}}return{rev:o.rev,truncated:!1}}a(dm,"searchAllFlows");async function zo(e,t,n={}){if(typeof t!="string"||!t.trim())throw new k("q query parameter is required","missing_query");let r=typeof n.cap=="number"&&n.cap>0?Math.floor(n.cap):Qh,o=[],s=n.flowId?await am(e,n.flowId,t,o,r):await dm(e,t,o,r),i={rev:s.rev,q:t,hits:o,hitCount:o.length,truncated:s.truncated,cap:r};return n.flowId&&(i.flowId=n.flowId),i}a(zo,"searchFlows");function yl(e){if(e===void 0)return!1;let t=e.toLowerCase();return t==="1"||t==="true"||t==="yes"}a(yl,"isTruthyQueryFlag");Pe();function lm(e,t,n){switch(e){case"node_not_found":case"flow_not_found":case"field_not_found":return n??we(e,t);default:return we(e,t)}}a(lm,"layeredReadErrorHint");function tn(e){return ot[e]??"Request failed"}a(tn,"safeErrorText");function _e(e,t,n){e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("Content-Type","application/json; charset=utf-8"),e.status(t).json(n)}a(_e,"sendJson");function Wo(e,t,n,r){let o=W(t,n);if(!o)return!1;let s=o.body.code,i={error:tn(s),code:s};typeof o.body.matchCount=="number"&&(i.matchCount=o.body.matchCount),typeof o.body.totalLines=="number"&&(i.totalLines=o.body.totalLines),typeof o.body.expectedRev=="string"&&(i.expectedRev=o.body.expectedRev),typeof o.body.currentRev=="string"&&(i.currentRev=o.body.currentRev);let d=lm(s,n,r);return d&&(i.hint=d),_e(e,o.statusCode,i),!0}a(Wo,"sendMappedError");function Go(e){_e(e,502,{error:tn("upstream_error"),code:"upstream_error"})}a(Go,"sendUpstreamError");function Ko(e,t="rest"){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){_e(r,400,{error:tn("missing_flow_id"),code:"missing_flow_id"});return}let s=yl(H(n.query,"summary"));try{if(s){_e(r,200,await Uo(Xe(n,e),o));return}_e(r,200,await He(Xe(n,e),o))}catch(i){if(Wo(r,i,t))return;Go(r)}}}a(Ko,"getFlowOrSummaryHandler");function Jo(e,t="rest"){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||!o){_e(r,400,{error:tn("missing_node_id"),code:"missing_node_id"});return}let s=H(n.query,"flow");try{_e(r,200,await Bo(Xe(n,e),o,s))}catch(i){if(Wo(r,i,t,t==="get"?"Call GET \u2026/flows/<id>?summary=1 or \u2026/search?q=\u2026":t==="mcp"?void 0:"Call GET \u2026/flows/<id>?summary=1 or \u2026/search?q=\u2026"))return;Go(r)}}}a(Jo,"getNodeHandler");function Vo(e,t="rest"){return async(n,r)=>{let o=n.params.id,s=n.params.name;if(typeof o!="string"||!o){_e(r,400,{error:tn("missing_node_id"),code:"missing_node_id"});return}if(typeof s!="string"||!s){_e(r,400,{error:tn("missing_field"),code:"missing_field"});return}let i=H(n.query,"flow"),d=H(n.query,"lines");try{_e(r,200,await qo(Xe(n,e),o,s,{...i?{flowId:i}:{},...d?{lines:d}:{}}))}catch(l){if(Wo(r,l,t,"Use summary/search to discover node ids and fields."))return;Go(r)}}}a(Vo,"getNodeFieldHandler");function Yo(e,t="rest"){return async(n,r)=>{let o=H(n.query,"q");if(!o){_e(r,400,{error:tn("missing_query"),code:"missing_query",hint:t==="get"?"Example: GET \u2026/search?q=TODO&flow=tab1":we("missing_query",t)});return}let s=H(n.query,"flow");try{_e(r,200,await zo(Xe(n,e),o,s?{flowId:s}:{}))}catch(i){if(Wo(r,i,t))return;Go(r)}}}a(Yo,"searchFlowsHandler");se();Ut();var cm={missing_node_type:"Missing node type",missing_query:"q query parameter is required",node_type_not_found:"Node type was not found",catalogue_unavailable:"Node-RED catalogue unavailable",unknown_node_type:"Unknown node type",upstream_error:"Upstream error"};function _r(e){return cm[e]??"Request failed"}a(_r,"safeErrorText");function Et(e,t,n){e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("Content-Type","application/json; charset=utf-8"),e.setHeader("Cache-Control","no-store"),e.status(t).json(n)}a(Et,"sendJson");function Ei(e,t){let n=W(t);if(!n)return!1;let r=n.body.code,o={error:_r(r),code:r};return typeof n.body.type=="string"&&(o.type=n.body.type),Array.isArray(n.body.nearest)&&(o.nearest=n.body.nearest),typeof n.body.didYouMean=="string"?(o.didYouMean=n.body.didYouMean,o.caseSensitive=!0,o.hint=`Types are case-sensitive \u2014 use '${n.body.didYouMean}' exactly.`):(r==="node_type_not_found"||r==="unknown_node_type")&&(o.hint="Call GET \u2026/node-types?q= or \u2026/palette/search?q=. Ask the user to install via Manage palette, then recheck node-types. This API never installs."),r==="catalogue_unavailable"&&(o.hint="Retry later; palette search is recommend-only and never installs packages."),Et(e,n.statusCode,o),!0}a(Ei,"sendMappedError");function Xo(e){return async(t,n)=>{try{let r=H(t.query,"q"),o=await po(e.nodeTypes,r?{q:r}:{});Et(n,200,o)}catch(r){if(Ei(n,r))return;Et(n,502,{error:_r("upstream_error"),code:"upstream_error"})}}}a(Xo,"listNodeTypesHandler");function Zo(e){return async(t,n)=>{try{let r=t.params.type,o=typeof r=="string"?decodeURIComponent(r):"",s=H(t.query,"help"),i=!(s==="0"||s==="false"||s==="no"||s==="omit"),d=await jt(e.nodeTypes,o,{includeHelp:i});Et(n,200,d)}catch(r){if(Ei(n,r))return;Et(n,502,{error:_r("upstream_error"),code:"upstream_error"})}}}a(Zo,"getNodeTypeHandler");function Qo(e){return async(t,n)=>{try{let r=H(t.query,"q");if(!r){Et(n,400,{error:_r("missing_query"),code:"missing_query",hint:"Pass ?q= to search the Node-RED catalogue (recommend only)."});return}let o=await fo(e.nodeTypes,r,{...e.catalogueFetcher?{catalogueFetcher:e.catalogueFetcher}:{},...e.catalogueUrl?{catalogueUrl:e.catalogueUrl}:{}});Et(n,200,o)}catch(r){if(Ei(n,r))return;Et(n,502,{error:_r("upstream_error"),code:"upstream_error"})}}}a(Qo,"searchPaletteHandler");Wt();Te();Ve();function Un(e,t){return e[t]??""}a(Un,"lineAt");function Cr(e,t,n){let r=e[t];return r?r[n]??0:0}a(Cr,"cell");function wl(e,t,n,r){let o=e[t];o&&(o[n]=r)}a(wl,"setCell");function vl(e,t){let n=bl(e),r=bl(t),o=n.length,s=r.length,i=[];for(let u=0;u<=o;u+=1){let p=[];for(let f=0;f<=s;f+=1)p.push(0);i.push(p)}for(let u=o-1;u>=0;u-=1)for(let p=s-1;p>=0;p-=1)Un(n,u)===Un(r,p)?wl(i,u,p,Cr(i,u+1,p+1)+1):wl(i,u,p,Math.max(Cr(i,u+1,p),Cr(i,u,p+1)));let d=[],l=0,c=0;for(;l<o&&c<s;){let u=Un(n,l),p=Un(r,c);u===p?(d.push({op:"equal",text:u}),l+=1,c+=1):Cr(i,l+1,c)>=Cr(i,l,c+1)?(d.push({op:"remove",text:u}),l+=1):(d.push({op:"add",text:p}),c+=1)}for(;l<o;)d.push({op:"remove",text:Un(n,l)}),l+=1;for(;c<s;)d.push({op:"add",text:Un(r,c)}),c+=1;return d}a(vl,"diffLines");function bl(e){return e.length===0?[]:e.split(`
990
+ `)}a(bl,"splitLines");Oe();Pn();nt();Pe();var xl=new Set(["func","template","info","payload","code","text","format","syntax"]),um=new Set(["x","y","w","h","wires","g"]),pm=new Set(["w","h","g"]);function nn(e){if(typeof e.name=="string"&&e.name.length>0)return e.name;if(typeof e.label=="string"&&e.label.length>0)return e.label}a(nn,"nodeName");function kl(e){let t=new Map;for(let n of e.nodes??[])t.set(n.id,n);for(let n of e.configs??[])t.set(n.id,n);return t}a(kl,"collectNodes");function rs(e){if(e===void 0)return"undefined";if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(rs).join(",")}]`;let t=e;return`{${Object.keys(t).sort().map(r=>`${JSON.stringify(r)}:${rs(t[r])}`).join(",")}}`}a(rs,"stableStringify");function Sl(e,t){return rs(e)===rs(t)}a(Sl,"valuesEqual");function os(e){return typeof e=="string"?e:null}a(os,"asText");function fm(e){return/<\/?[a-z][\s\S]*>/i.test(e.slice(0,800))}a(fm,"looksLikeHtml");function hm(e,t,n){if(t!=="template")return!1;let r=typeof e.format=="string"?e.format.toLowerCase():"";return r==="html"||r==="handlebars"||r==="mustache"?!0:fm(n)}a(hm,"isHtmlTemplateField");function Tl(e,t,n){let r=os(n);if(xl.has(t)&&r!==null){let o={key:t,kind:"text",value:r};return hm(e,t,r)&&(o.preview="html"),o}return{key:t,kind:"scalar",value:n===void 0?null:n}}a(Tl,"toPresenceProperty");function mm(e,t){let n=[];for(let r of[...Object.keys(e)].sort())if(!(pm.has(r)||r==="id"||r==="z")){if(r==="wires"){let o=Rl(e,t);n.push({key:"wires",kind:"scalar",value:o.length>0?o:e.wires??null});continue}n.push(Tl(e,r,e[r]))}return n}a(mm,"nodeProperties");function Rl(e,t){let n=e.wires;if(!Array.isArray(n)||n.length===0)return[];let r=[];return n.forEach((o,s)=>{if(!Array.isArray(o))return;let i=[];for(let d of o){if(typeof d!="string"||!d)continue;let l=t.get(d);i.push({id:d,name:l?nn(l)??l.type:d})}r.push({port:s,to:i})}),r}a(Rl,"describeWiresStructured");var gm=new Set(["func","template","code","text"]);function ym(e,t){let n=[],r=nn(e);r&&n.push({key:"name",kind:"scalar",value:r});let o=Rl(e,t);o.length>0&&n.push({key:"wires",kind:"scalar",value:o});for(let s of gm){if(!(s in e))continue;let i=os(e[s]);i===null||i.length===0||n.push(Tl(e,s,i))}return n}a(ym,"buildKeyFields");function wm(e,t,n){let r=vt(e.type),o=nn(e),s=o?` "${o}"`:"",i=t?` in "${t}"`:"",d=n===1?"1 field":`${n} fields`;return`Changes ${r} node${s}${i} (${d})`}a(wm,"buildModifiedSummary");function bm(e,t,n){let r=vt(e.type),o=nn(e),s=t==="added"?"Adds":"Removes",i=t==="added"?"to":"from",d=o?` ${JSON.stringify(o)}`:"",l=n?` ${i} flow ${JSON.stringify(n)}`:"",c=`${s} ${r} node${d}${l}`;if(t==="added"&&r==="template"){let u=typeof e.field=="string"&&e.field?e.field:"";u&&(c+=`, output to msg.${u}`)}return c}a(bm,"buildPresenceSummary");function es(e,t,n,r){let o={id:e.id,type:e.type,summary:bm(e,t,n),keyFields:ym(e,r),properties:mm(e,r)},s=nn(e);return s!==void 0&&(o.name=s),o}a(es,"toPresenceNode");function vm(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let o of[...n].sort()){if(um.has(o)||o==="id"||o==="z")continue;let s=e[o],i=t[o];if(Sl(s,i))continue;let d=os(s),l=os(i);if(xl.has(o)&&(d!==null||l!==null)){let c=d??"",u=l??"";r.push({key:o,kind:"text",before:c,after:u,lines:vl(c,u)});continue}r.push({key:o,kind:"scalar",before:s===void 0?null:s,after:i===void 0?null:i})}return r}a(vm,"compareNodes");function Al(e,t){let n=["label","disabled","info","env"],r=[];for(let o of n){let s=o==="disabled"?!1:o==="env"?[]:"";Sl(e[o]??s,t[o]??s)||r.push(o)}return r}a(Al,"compareTabMeta");function ts(e){return typeof e.label=="string"&&e.label.trim()?e.label.trim():""}a(ts,"flowLabelOf");function Er(e,t){let n=t.match(/\["(?:[^"\\]|\\.)*"\]|[^.[\]]+/g)??[],r=e;for(let o of n)if(o.startsWith("[")){let s=JSON.parse(o.slice(1,-1));r=Array.isArray(r)?r.find(i=>!!i&&typeof i=="object"&&i.name===s):void 0}else r=r&&typeof r=="object"&&Object.hasOwn(r,o)?r[o]:void 0;return r}a(Er,"conflictFieldValue");function ns(e,t){let n=kl(t),r=kl(e),o=ts(e)||ts(t),s=ts(t)||ts(e),i=[],d=[],l=[];for(let c of e.nodes??[])n.has(c.id)||i.push(es(c,"added",o,r));for(let c of e.configs??[])n.has(c.id)||i.push(es(c,"added",o,r));for(let c of t.nodes??[])r.has(c.id)||d.push(es(c,"removed",s,n));for(let c of t.configs??[])r.has(c.id)||d.push(es(c,"removed",s,n));for(let[c,u]of r){let p=n.get(c);if(!p)continue;let f=vm(p,u);if(f.length>0){let m={id:c,type:u.type||p.type,summary:wm(u,o||s,f.length),changes:f},h=nn(u)??nn(p);h!==void 0&&(m.name=h),l.push(m)}}return l.sort((c,u)=>c.id.localeCompare(u.id)),{summary:{added:i.length,removed:d.length,modified:l.length,tabChanges:Al(t,e),beforeNodeCount:n.size,afterNodeCount:r.size},tabProperties:Al(t,e).map(c=>({key:c,kind:"scalar",before:t[c],after:e[c]})),added:i,removed:d,modified:l}}a(ns,"buildDiffSections");function Ii(e,t,n={}){let r=ns(e.payload.flow,t.flow),o=n.currentRev??t.rev,s=e.expectedRev,i=n.baseRevChanged??(U(e.status)&&s!==o),d={proposalId:e.id,flowId:e.payload.flowId,expectedRev:s,baseRev:s,currentRev:o,baseRevChanged:i,compareAgainst:n.compareAgainst??"current",...r};return e.warnings&&e.warnings.length>0&&(d.warnings=e.warnings.map(l=>({...l}))),n.baseSnapshotMissing&&(d.baseSnapshotMissing=!0),n.vsCurrent&&(d.vsCurrent=n.vsCurrent),d}a(Ii,"buildProposalDiff");async function Bn(e,t,n=e){let r=e.payload.flowId,o={id:r,type:"tab",nodes:[],configs:[]},s=e.expectedRev,i=o;try{let u=await t.getFlow(r);s=u.rev,i=u.flow}catch(u){if(!(u instanceof X))throw u;try{s=(await t.getFlows()).rev}catch{}}let d=U(e.status),l=d&&e.expectedRev!==s,c=e.baseFlow;if(d){let u=$e(t),p=await u.getFlows(),f=di(n,p),m=u!==t,h=m?Ye(n):n,g=zt(p.flows,r,n.baseFlow)??o,y=m?ct(g).value:g,w=Ii(h,{rev:e.expectedRev,flow:h.baseFlow??y},{currentRev:p.rev,compareAgainst:h.baseFlow?"base":"current",baseSnapshotMissing:h.baseFlow===void 0,baseRevChanged:e.expectedRev!==p.rev});w.rebase={rev:f.rev,state:f.state,conflicts:m?ti(f.conflicts):f.conflicts,...f.mergedHash?{mergedHash:f.mergedHash}:{}};let v=a((P,I)=>I===r?P:[...P?.nodes??[],...P?.configs??[]].find(D=>D.id===I),"target");w.conflictValues=f.conflicts.map(P=>({nodeId:P.nodeId??"",fields:(P.fields?.length?P.fields:[""]).map(I=>({field:I,base:I?Er(v(h.baseFlow,P.nodeId),I):v(h.baseFlow,P.nodeId),live:I?Er(v(y,P.nodeId),I):v(y,P.nodeId),proposed:I?Er(v(h.payload.flow,P.nodeId),I):v(h.payload.flow,P.nodeId)}))}));let L=f.merged?m?ct(f.merged).value:f.merged:h.payload.flow;return w.aiOverrideValues=(h.aiConflictOverrides??[]).map(P=>({nodeId:P.nodeId,fields:P.fields.map(I=>({field:I,live:I?Er(v(y,P.nodeId),I):v(y,P.nodeId),proposed:I?Er(v(L,P.nodeId),I):v(L,P.nodeId)}))})),w.liveChanges=ns(y,h.baseFlow??y),f.state!=="conflict"&&f.merged&&(w.vsCurrent=ns(m?ct(f.merged).value:f.merged,y)),w}if(c!==void 0){let u=l?ns(e.payload.flow,i):void 0;return Ii(e,{rev:e.expectedRev,flow:c},{currentRev:s,compareAgainst:"base",baseRevChanged:l,...u?{vsCurrent:u}:{}})}return Ii(e,{rev:s,flow:i},{compareAgainst:"current",baseSnapshotMissing:!0,baseRevChanged:l})}a(Bn,"computeProposalDiff");Oe();nt();ze();ke();var Pl=require("path");function ss(e){return(0,Pl.join)(e.userDir,".ai-access")}a(ss,"getStorageDir");var km=["status:read","flows:read","proposals:create","proposals:read-own"];function Li(){return On()}a(Li,"readPackageVersion");function Am(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/1e3)):0}a(Am,"secondsRemainingUntil");function xm(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/6e4)):0}a(xm,"minutesRemainingUntil");function Sm(e,t){let n=xe(t),r=mt(t),o=`${n}/openapi.yaml`,s=`${n}/openapi.json`;return e==="rest"?{api:n,openapi:o,openapiJson:s,discovery:n}:e==="mcp"?{rpc:r,discovery:n}:{discovery:n}}a(Sm,"baseUrlsForTokenKind");function qn(e,t){let n=Yr(e,t),r=Ce(e.settings);if(t.auth){let o=t.auth.extra?.user;return{ok:!0,version:Li(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:t.auth.scopes.includes("propose")?"propose":"read",capabilities:{view:!0,manageLinks:!1,approveProposals:!1},defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,maxActiveLinks:r.maxActiveLinks,defaultDeploymentType:r.defaultDeploymentType,caller:{kind:"oauth",clientId:t.auth.clientId,user:typeof o=="string"?o:"owner",scopes:t.auth.scopes}}}if(t.accessLink){let o=Date.now();return{ok:!0,version:Li(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:null,capabilities:{view:!0,manageLinks:!1,approveProposals:!1},defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,maxActiveLinks:r.maxActiveLinks,defaultDeploymentType:r.defaultDeploymentType,caller:{kind:"access-link",linkId:t.accessLink.id,tokenKind:t.accessLink.kind,...t.accessLink.label!==void 0?{label:t.accessLink.label}:{},expiresAt:t.accessLink.expiresAt,minutesRemaining:xm(t.accessLink.expiresAt,o),secondsRemaining:Am(t.accessLink.expiresAt,o),permissions:[...km],baseUrls:Sm(t.accessLink.kind,e.settings.httpAdminRoot)}}}return{ok:!0,version:Li(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:n.permission,capabilities:n.capabilities,storageDir:ss(e.settings),defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,maxActiveLinks:r.maxActiveLinks,defaultDeploymentType:r.defaultDeploymentType,caller:{kind:"admin",...n.permission?{permission:n.permission}:{}}}}a(qn,"buildStatus");function _l(e,t){let n=t.draftStore??new Mo,r=t.rateLimiters??rl(t.rateLimit??e.settings.aiAccess),o=e.httpAdmin,s="/mcp/a/:token",i=[r.ip,sl(e,t.linkStore),r.token];o.use("/mcp/a",zd()),o.get(s,...i,$((u,p)=>{z(p),p.setHeader("X-Content-Type-Options","nosniff");let f=xe(e.settings.httpAdminRoot),m=u.accessLink;if(!m){j(p,401,"Access link required","access_link_missing");return}let h=Xd({link:m,openapiPath:`${f}/openapi.yaml`,restApiBase:f,mcpRpcPath:mt(e.settings.httpAdminRoot),publicOrigin:xs(u)});p.status(200).type("text/markdown; charset=utf-8").send(h)})),o.get(`${s}/status`,...i,$((u,p)=>{z(p),p.json(qn(e,u))})),o.get(`${s}/flows`,...i,$(async(u,p)=>{z(p);try{p.json(await pt(t.flowAdapter))}catch(f){let m=W(f);if(m){p.status(m.statusCode).json({...m.body,hint:"Retry after the owner fixes the plugin runtime."});return}j(p,502,f instanceof Error?f.message:String(f),"upstream_error")}})),o.get(`${s}/flows/:id`,...i,$(async(u,p)=>{z(p),await Ko(t.flowAdapter,"get")(u,p)})),o.get(`${s}/nodes/:id`,...i,$(async(u,p)=>{z(p),await Jo(t.flowAdapter,"get")(u,p)})),o.get(`${s}/nodes/:id/field/:name`,...i,$(async(u,p)=>{z(p),await Vo(t.flowAdapter,"get")(u,p)})),o.get(`${s}/search`,...i,$(async(u,p)=>{z(p),await Yo(t.flowAdapter,"get")(u,p)}));let d={nodeTypes:t.nodeTypesAdapter,...t.catalogueFetcher?{catalogueFetcher:t.catalogueFetcher}:{},...t.catalogueUrl?{catalogueUrl:t.catalogueUrl}:{}};o.get(`${s}/node-types`,...i,$(async(u,p)=>{z(p),await Xo(d)(u,p)})),o.get(`${s}/node-types/:type`,...i,$(async(u,p)=>{z(p),await Zo(d)(u,p)})),o.get(`${s}/palette/search`,...i,$(async(u,p)=>{z(p),await Qo(d)(u,p)}));let l=e.settings.httpAdminRoot;Tm(o,s,i,t,l),o.get(`${s}/propose`,...i,$(pl(t.proposalStore,t.auditLog,t.flowAdapter,l,t.nodeTypesAdapter))),_m(o,s,i,n,t,l);let c=al();return o.all(`${s}/links`,c),o.all(`${s}/links/:id`,c),o.use("/mcp/a",il()),n}a(_l,"registerGetLinkRoutes");function Tm(e,t,n,r,o){e.get(`${t}/proposals`,...n,$(async(s,i)=>{z(i);let d=s.accessLink;if(!d){j(i,401,"Access link required","access_link_missing");return}let l=V(s,o),c=ge(s.query),u=_o(s.query),p=await tt(r.flowAdapter),f=p?De(r.proposalStore,p).expired:te(r.proposalStore),m=En(r.proposalStore,d,l,u);i.json({proposals:m.proposals.map(h=>he(r.proposalStore,d,h,{...l,include:c},{includeConflicts:!1})),...Jt({...m,returned:m.proposals.length}),...pe(f)})})),e.get(`${t}/proposals/:id`,...n,$(async(s,i)=>{z(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){j(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){j(i,401,"Access link required","access_link_missing");return}try{let c=V(s,o),u=ge(s.query),p=await tt(r.flowAdapter),f=ut(r.proposalStore,l,d,c);i.json(he(r.proposalStore,l,f,{...c,include:u},{...p?{live:p}:{}}))}catch(c){let u=W(c,"get");if(u){i.status(u.statusCode).json({...u.body,hint:u.body.hint??"List proposals with GET \u2026/proposals."});return}B(i,c);return}})),Rm(e,t,n,r,o),Pm(e,t,n,r,o)}a(Tm,"registerGetLinkProposalRoutes");function Rm(e,t,n,r,o){e.get(`${t}/proposals/:id/withdraw`,...n,$((s,i)=>{z(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){j(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){j(i,401,"Access link required","access_link_missing");return}let c=s.query.confirm;if(c!=="1"&&c!=="true"){j(i,400,"Withdraw over GET requires confirm=1","confirm_required",we("confirm_required","get"));return}let u=typeof s.query.reason=="string"?s.query.reason:void 0,p=V(s,o),f=ge(s.query);try{let m=In(r.proposalStore,r.auditLog,l,d,u,p);i.json(he(r.proposalStore,l,m,{...p,include:f}))}catch(m){let h=W(m,"get");if(h){let g=h.body.code;j(i,h.statusCode,ot[g]??"Request failed",g,we(g,"get"));return}j(i,500,"Internal server error","internal_error");return}}))}a(Rm,"registerGetLinkWithdrawRoute");function Pm(e,t,n,r,o){e.get(`${t}/proposals/:id/diff`,...n,$(async(s,i)=>{z(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){j(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){j(i,401,"Access link required","access_link_missing");return}try{let c=ut(r.proposalStore,l,d,V(s,o)),u=H(s.query,"format"),p=Mn(u==="json"||u==="md"?void 0:u);if(!p.ok){j(i,400,"Invalid diff format","invalid_diff_format","Valid formats: summary, full (default summary).");return}i.json(Dn(await Bn(c,r.flowAdapter,r.proposalStore.get(d)),p.format))}catch(c){let u=W(c);if(u){i.status(u.statusCode).json(u.body);return}B(i,c);return}}))}a(Pm,"registerGetLinkDiffRoute");function _m(e,t,n,r,o,s){e.get(`${t}/draft/new`,...n,$(ll(r,o.flowAdapter))),e.get(`${t}/draft/:draftId/append`,...n,$(cl(r))),e.get(`${t}/draft/:draftId/submit`,...n,$(ul(r,o.proposalStore,o.auditLog,o.flowAdapter,s,o.nodeTypesAdapter)))}a(_m,"registerGetLinkDraftRoutes");oe();var Cl=require("module");function Cm(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Cl.createRequire)(e)}a(Cm,"createPluginRequire");function Em(e){try{let r=Cm()("@node-red/runtime")._?.adminApi?.httpAdmin;if(r&&typeof r.use=="function"&&r!==e.httpAdmin)return r}catch{}return null}a(Em,"resolveEditorHttpAdmin");function El(e){let t=[e.httpAdmin],n=Em(e);return n&&t.push(n),t}a(El,"httpAdminAppsForMcpErrors");var Il=Symbol.for("com55.ai-access.mcpJsonErrorHandler");function Im(e){let t=e??"/";for(;t.endsWith("/");)t=t.slice(0,-1);let n=new Set;for(let r of t?["",t]:[""])for(let o of["/mcp","/mcp/rpc"])n.add(r+o),n.add(`${r+o}/`);return r=>n.has(r)}a(Im,"mcpRpcMatcher");function Ni(e){return e.includes("/mcp")}a(Ni,"isMcpPath");function Lm(e){return e.path||e.url||e.originalUrl||""}a(Lm,"requestPath");function Nm(e){if(!e||typeof e!="object")return!1;let t=e;if(t.type==="entity.parse.failed"||e instanceof SyntaxError)return!0;let n=typeof e.message=="string"?e.message:"";return/JSON/i.test(n)&&(t.status===400||t.statusCode===400)}a(Nm,"isJsonParseError");function Fm(e){if(!e||typeof e!="object")return!1;let t=e;return t.type==="entity.too.large"||t.status===413||t.statusCode===413||t.message==="request entity too large"}a(Fm,"isPayloadTooLarge");function Om(e){let t=Im(e);return(n,r,o,s)=>{if(o.headersSent){s(n);return}let i=Lm(r);if(Nm(n)&&Ni(i)){if(t(i)){o.status(400).json({jsonrpc:"2.0",error:{code:-32700,message:"Parse error"},id:null});return}o.status(400).json({error:"Invalid JSON body",code:"invalid_json"});return}if(Fm(n)&&Ni(i)){o.status(413).json({error:`Request body exceeds limit of ${K} bytes`,code:"payload_too_large"});return}if(Ni(i)){if(t(i)){o.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:$o(n).error,data:{code:"internal_error"}},id:null});return}o.status(500).json($o(n));return}s(n)}}a(Om,"mcpJsonErrorHandler");function $m(e,t){let n=e;n[Il]||(n[Il]=!0,e.use(Om(t)))}a($m,"attachOnce");function Ll(e){for(let t of El(e))$m(t,e.settings.httpAdminRoot)}a(Ll,"attachMcpJsonErrorHandlers");Rr();var rn=class extends Error{static{a(this,"AccessLinkLimitError")}code="access_link_limit";statusCode=409;constructor(t){super(`Cannot create access link: at most ${t} active links are allowed. Revoke an existing link first.`),this.name="AccessLinkLimitError"}},on=class extends Error{static{a(this,"AccessLinkNotFoundError")}code="access_link_not_found";statusCode=404;constructor(t){super(`Access link '${t}' was not found`),this.name="AccessLinkNotFoundError"}},Ir=class extends Error{static{a(this,"AccessLinkValidationError")}code="access_link_invalid";statusCode=400;constructor(t){super(t),this.name="AccessLinkValidationError"}},It=class extends Error{static{a(this,"AccessLinkTtlError")}code="invalid_ttl";statusCode=400;hint=Vt;constructor(t){super(t),this.name="AccessLinkTtlError"}};var sn=require("crypto");Rr();function Fi(e){return e==="get_link"||e==="rest"||e==="mcp"}a(Fi,"isAccessLinkKind");function Oi(e){return e??"get_link"}a(Oi,"resolveAccessLinkKind");function Nl(e){return(0,sn.createHash)("sha256").update(e,"utf8").digest("hex")}a(Nl,"hashToken");function Mm(e,t){let n=Buffer.from(e,"utf8"),r=Buffer.from(t,"utf8");return n.length!==r.length?!1:(0,sn.timingSafeEqual)(n,r)}a(Mm,"hashesEqual");function Lr(e){let t={id:e.id,kind:Oi(e.kind),createdAt:new Date(e.createdAtMs).toISOString(),expiresAt:new Date(e.expiresAtMs).toISOString()};return e.label!==void 0&&(t.label=e.label),e.lastUsedAtMs!==void 0&&(t.lastUsedAt=new Date(e.lastUsedAtMs).toISOString()),e.createdBy!==void 0&&(t.createdBy=e.createdBy),t}a(Lr,"toPublic");var an=class{static{a(this,"AccessLinkStore")}links=new Map;defaultTtlSeconds;maxActiveLinks;now;randomId;randomToken;constructor(t={}){this.defaultTtlSeconds=t.defaultTtlSeconds??14400,this.maxActiveLinks=t.maxActiveLinks??10,this.now=t.now??(()=>Date.now()),this.randomId=t.randomId??(()=>(0,sn.randomBytes)(12).toString("hex")),this.randomToken=t.randomToken??(()=>`aia_${(0,sn.randomBytes)(32).toString("base64url")}`)}configureDefaults(t){typeof t.defaultTtlSeconds=="number"&&Number.isFinite(t.defaultTtlSeconds)&&t.defaultTtlSeconds>0&&(this.defaultTtlSeconds=Math.floor(t.defaultTtlSeconds)),typeof t.maxActiveLinks=="number"&&Number.isFinite(t.maxActiveLinks)&&t.maxActiveLinks>0&&(this.maxActiveLinks=Math.floor(t.maxActiveLinks))}isActive(t,n){return t.revokedAtMs!==void 0?!1:t.expiresAtMs>n}list(){let t=this.now();return[...this.links.values()].filter(n=>this.isActive(n,t)).sort((n,r)=>r.createdAtMs-n.createdAtMs).map(Lr)}create(t={}){let n=this.now();if([...this.links.values()].filter(p=>this.isActive(p,n)).length>=this.maxActiveLinks)throw new rn(this.maxActiveLinks);let o=this.defaultTtlSeconds;if(t.ttlSeconds!==void 0){if(typeof t.ttlSeconds!="number"||!Number.isFinite(t.ttlSeconds)||t.ttlSeconds<=0)throw new It("ttlSeconds must be a positive number");o=Math.floor(t.ttlSeconds)}let s=typeof t.label=="string"&&t.label.trim().length>0?t.label.trim():void 0,i=Oi(t.kind),d=wi(i);if(o>d)throw new It(bi(i,d));let l=this.randomId(),c=this.randomToken(),u={id:l,tokenHash:Nl(c),kind:i,createdAtMs:n,expiresAtMs:n+o*1e3};return s!==void 0&&(u.label=s),t.createdBy!==void 0&&(u.createdBy=t.createdBy),this.links.set(l,u),{...Lr(u),token:c}}revoke(t){let n=this.links.get(t);if(!n)throw new on(t);return n.revokedAtMs??=this.now(),Lr(n)}getById(t){if(typeof t!="string"||t.length===0)return null;let n=this.links.get(t);return n?Lr(n):null}validateResult(t){if(typeof t!="string"||t.length===0)return{status:"invalid"};let n=Nl(t),r=this.now();for(let o of this.links.values())if(Mm(o.tokenHash,n))return o.revokedAtMs!==void 0?{status:"invalid"}:o.expiresAtMs<=r?{status:"expired",expiresAt:new Date(o.expiresAtMs).toISOString()}:(o.lastUsedAtMs=r,{status:"active",link:Lr(o)});return{status:"invalid"}}validate(t){let n=this.validateResult(t);return n.status==="active"?n.link:null}size(){return this.links.size}clear(){this.links.clear()}upsertRecordForTest(t){let n={...t};delete n.token,this.links.set(t.id,n)}};Rr();ve();ke();function $i(e,t){return t}a($i,"storeForRequest");function Ol(e){let t=typeof e.user?.username=="string"?e.user.username:void 0;return t?{kind:"admin",username:t}:{kind:"admin"}}a(Ol,"adminActor");function is(e){let t=Ce(e.settings);return new an({defaultTtlSeconds:t.defaultLinkTtlSeconds,maxActiveLinks:t.maxActiveLinks})}a(is,"createConfiguredLinkStore");function $l(e){e.setHeader("Cache-Control","no-store"),e.setHeader("Pragma","no-cache")}a($l,"noStore");function Ml(e,t){return(n,r)=>{let s=$i(e,t).list();$l(r),r.json({links:s})}}a(Ml,"listLinksHandler");function Dl(e,t,n){return(r,o)=>{let s=r.body??{},i={};if(s.ttlSeconds!==void 0){if(typeof s.ttlSeconds!="number"){o.status(400).json({error:"ttlSeconds must be a positive number",code:"invalid_ttl",hint:Vt});return}i.ttlSeconds=s.ttlSeconds}if(typeof s.label=="string"&&(i.label=s.label),s.kind!==void 0){if(!Fi(s.kind)){o.status(400).json({error:"kind must be one of: 'get_link', 'rest', 'mcp'",code:"invalid_link_kind"});return}i.kind=s.kind}typeof r.user?.username=="string"&&(i.createdBy=r.user.username);try{let d=$i(e,t).create(i),l=d.kind;if(n){let c=Date.parse(d.createdAt),u=Date.parse(d.expiresAt),p=Number.isFinite(c)&&Number.isFinite(u)&&u>c?Math.round((u-c)/1e3):void 0;n.append("link.created",Ol(r),{linkId:d.id,label:d.label,expiresAt:d.expiresAt,createdAt:d.createdAt,kind:l,...p!==void 0?{ttlSeconds:p}:{}})}if($l(o),l==="get_link"){let c=wa(r,e.settings.httpAdminRoot,d.token);o.status(201).json({...d,url:c});return}o.status(201).json({...d})}catch(d){if(d instanceof rn){o.status(d.statusCode).json({error:d.message,code:d.code});return}if(d instanceof It){o.status(d.statusCode).json({error:d.message,code:d.code,hint:d.hint});return}if(d instanceof Ir){o.status(d.statusCode).json({error:d.message,code:d.code});return}B(o,d);return}}}a(Dl,"createLinkHandler");function Hl(e,t,n){return(r,o)=>{let s=r.params.id;if(typeof s!="string"||s.length===0){o.status(400).json({error:"Missing link id"});return}try{let i=$i(e,t).revoke(s);if(n){let d=Date.parse(i.createdAt),l=Date.parse(i.expiresAt),c=Number.isFinite(d)&&Number.isFinite(l)&&l>d?Math.round((l-d)/1e3):void 0;n.append("link.revoked",Ol(r),{linkId:i.id,label:i.label,expiresAt:i.expiresAt,createdAt:i.createdAt,kind:i.kind,...c!==void 0?{ttlSeconds:c}:{}})}o.json({revoked:!0,link:i})}catch(i){if(i instanceof on){o.status(i.statusCode).json({error:i.message,code:i.code});return}B(o,i);return}}}a(Hl,"revokeLinkHandler");function Dm(e){let t=Date.parse(e.createdAt),n=Date.parse(e.expiresAt);if(Number.isFinite(t)&&Number.isFinite(n)&&n>t)return Math.round((n-t)/1e3)}a(Dm,"ttlSecondsFromLink");function Hm(e,t){if(!t||e.action!=="link.created"&&e.action!=="link.revoked")return e;let n={...e.details??{}},r=typeof n.linkId=="string"?n.linkId:"";if(!r)return e;let o=n.label==null||n.label==="",s=n.expiresAt==null||n.expiresAt==="",i=n.createdAt==null||n.createdAt==="",d=n.ttlSeconds==null;if(!o&&!s&&!i&&!d)return e;let l=t.getById(r);if(!l)return e;if(o&&l.label&&(n.label=l.label),s&&(n.expiresAt=l.expiresAt),i&&(n.createdAt=l.createdAt),d){let c=Dm(l);c!==void 0&&(n.ttlSeconds=c)}return{...e,details:n}}a(Hm,"enrichAuditLogEntry");function jl(e,t){return(n,r)=>{let o=n.query.limit,s=100;if(typeof o=="string"&&o.length>0){let d=Number(o);Number.isFinite(d)&&d>0&&(s=Math.min(Math.floor(d),500))}let i=e.list(s).map(d=>Hm(d,t));r.json({logs:i})}}a(jl,"listLogsHandler");var Di=require("@modelcontextprotocol/sdk/server/mcp.js"),Jl=require("@modelcontextprotocol/sdk/server/streamableHttp.js"),_=require("zod");Ve();oe();Mt();se();rt();Ut();Wt();Oe();nt();var Ul=require("async_hooks");Pe();var jm="ai.redacted",Bl=new Ul.AsyncLocalStorage;function ql(e,t,n,r){if(!(r<=0))try{e.append(jm,t,{redactions:r,operation:n})}catch{}}a(ql,"appendRedactionAudit");function zl(e){let t=Bl.getStore();if(!t)return;let n=ko(e);n>0&&t.record(n)}a(zl,"noteResponseRedactions");function Wl(e,t,n,r){return Bl.run({record:a(o=>ql(e,t,n,o),"record")},r)}a(Wl,"runWithRedactionAudit");function Gl(e){let t=Array.isArray(e)?e[0]:e;if(t&&typeof t=="object"){let n=t;if(n.method==="tools/call"&&typeof n.params?.name=="string")return`mcp:${n.params.name.replace(/[^A-Za-z0-9_.-]/g,"").slice(0,64)}`;if(typeof n.method=="string")return`mcp:${n.method.slice(0,64)}`}return"mcp"}a(Gl,"mcpOperationName");function Mi(e){let t=e.accessLink;if(t)return{kind:"access-link",linkId:t.id,...t.label!==void 0?{label:t.label}:{}}}a(Mi,"accessLinkAuditActor");function Kl(e){return(t,n,r)=>{if(!t.path.startsWith("/mcp/")||t.path.startsWith("/mcp/rpc")){r();return}let o=n.json.bind(n);n.json=(s=>{let i=Mi(t);if(i&&n.statusCode<400){let d;try{d=String(JSON.stringify(s))}catch{d=""}let l=t.route?.path,c=`${t.method} ${typeof l=="string"?l:"unknown"}`;ql(e,i,c,ko(d))}return o(s)}),r()}}a(Kl,"redactionAuditMiddleware");Pe();function Vl(){return"0.2.0-beta.8"}a(Vl,"pluginVersion");function ae(e){let t=JSON.stringify(e);return zl(t),{content:[{type:"text",text:t}]}}a(ae,"jsonResult");function Hi(e){return e===!0?new Set(["payload","baseFlow"]):new Set}a(Hi,"includeSetFromPayloadFlag");function de(e,t=500){let n=Tr(e,"mcp");if(n)return{isError:!0,content:[{type:"text",text:JSON.stringify({...n.body,statusCode:n.statusCode})}]};let r=e instanceof Error?e.message:String(e);return{isError:!0,content:[{type:"text",text:JSON.stringify({error:r,code:"internal_error",statusCode:t})}]}}a(de,"jsonError");var Um=new Set(["list_proposals","list_my_proposals"]),Bm=new Set(["status","limit","offset","mine","flowId"]);function qm(e){return!!e&&typeof e=="object"&&typeof e.safeParseAsync=="function"}a(qm,"isZodLikeSchema");function zm(e,t,n){let r=t&&typeof t=="object"?t:{},o=n[0],s=o?.path&&o.path.length>0?String(o.path[0]):"(arguments)";if(Um.has(e)&&Bm.has(s))return Cn(s,r[s]);let i=n.map(l=>({parameter:l.path&&l.path.length>0?l.path.map(String).join("."):"(arguments)",message:l.message??"Invalid value"})),d=i.length===1?`Invalid argument '${i[0]?.parameter}' for tool ${e}: ${i[0]?.message}`:`${i.length} invalid arguments for tool ${e}`;return new k(d,"invalid_arguments",{details:{parameter:s,errors:i,hint:`Fix '${s}' to match the ${e} input schema (see tools/list).`}})}a(zm,"mcpArgumentError");function Yl(e){let t=e,n=t.validateToolInput;typeof n=="function"&&(t.validateToolInput=async(r,o,s)=>{let i=r.inputSchema;if(!qm(i))return n.call(e,r,o,s);let d=await i.safeParseAsync(o??{});if(d.success)return d.data;let l=Tr(zm(s,o,d.error.issues),"mcp"),c=l?{...l.body,statusCode:l.statusCode}:{};throw new Error(JSON.stringify(c))})}a(Yl,"installMcpArgumentErrorMapping");function Xl(e,t,n,r){e.registerTool("get_status",{description:'Return plugin status (version, AI feature flags, storage path, deploy defaults). For access-link callers, permission is null and caller.kind is "access-link" \u2014 that is expected, not an error. Use before other tools to confirm the plugin is enabled.'},()=>{try{return ae(qn(t,r))}catch(o){return de(o)}}),e.registerTool("list_flows",{description:'List all flows as a flat Admin API v2 array plus the current rev string. Likely secrets are shown as "[REDACTED]" (nodes list redacted paths in _aiRedactions). Returns { rev, flows }. Call this (or get_flow) before submit_proposal so expectedRev matches.'},async()=>{try{return ae(await pt(n.flowAdapter))}catch(o){return de(o,502)}}),e.registerTool("get_flow",{description:"Get one tab/subflow by id with nested nodes/configs and the current rev. Pass summary=true for a lightweight node list (id/type/name/wires/bytes, no field contents). Prefer summary \u2192 search_flows \u2192 get_node_field(lines) \u2192 replace_text \u2192 submit_proposal. Returns { rev, flow } or summary shape. Use rev as expectedRev when submitting.",inputSchema:{id:_.z.string().min(1).describe("Flow / tab id"),summary:_.z.boolean().optional().describe("When true, return node list with sizes only (no field contents)")}},async({id:o,summary:s})=>{try{return ae(s?await Uo(n.flowAdapter,o):await He(n.flowAdapter,o))}catch(i){return de(i,502)}}),e.registerTool("get_node",{description:"Get one full node by id (includes owning flowId when known). Optional flow scopes the lookup.",inputSchema:{id:_.z.string().min(1).describe("Node id"),flow:_.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,flow:s})=>{try{return ae(await Bo(n.flowAdapter,o,s))}catch(i){return de(i,502)}}),e.registerTool("get_node_field",{description:'Get one string field from a node. Optional lines="A-B" (1-based inclusive) returns a slice; response always includes totalLines and bytes.',inputSchema:{id:_.z.string().min(1).describe("Node id"),field:_.z.string().min(1).describe("Field name (e.g. template, func)"),lines:_.z.string().optional().describe("Optional 1-based line range A or A-B"),flow:_.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,field:s,lines:i,flow:d})=>{try{return ae(await qo(n.flowAdapter,o,s,{...d?{flowId:d}:{},...i?{lines:i}:{}}))}catch(l){return de(l,502)}}),e.registerTool("search_flows",{description:"Text search across string fields. Returns hits with node id, field, line numbers, and a short snippet. Results are capped; truncated=true when the cap is hit. Optional flow scopes to one tab.",inputSchema:{q:_.z.string().describe("Substring to find (case-sensitive; non-empty required)"),flow:_.z.string().min(1).optional().describe("Optional tab id")}},async({q:o,flow:s})=>{try{return ae(await zo(n.flowAdapter,o,s?{flowId:s}:{}))}catch(i){return de(i,502)}}),e.registerTool("list_node_types",{description:"List installed Node-RED palette node types (type, module, category, description, \u2026). Optional q filters type/module/category/description (case-insensitive). Results are capped. Call this before proposing nodes with unfamiliar types.",inputSchema:{q:_.z.string().optional().describe("Optional case-insensitive filter")}},async({q:o})=>{try{return ae(await po(n.nodeTypesAdapter,o?{q:o}:{}))}catch(s){return de(s,502)}}),e.registerTool("get_node_type",{description:"Get detail for one installed node type: defaults (JSON), inputs/outputs (always set), requiredConfigNodeTypes, help sections (summary/inputs/outputs), category/module/color. Types are case-sensitive. Pass includeHelp=false to omit help. 404 node_type_not_found when unknown (didYouMean on case mismatch; else nearest names).",inputSchema:{type:_.z.string().min(1).describe("Node type id (e.g. inject, template) \u2014 case-sensitive"),includeHelp:_.z.boolean().optional().describe("When false, omit help sections (default true)")}},async({type:o,includeHelp:s})=>{try{return ae(await jt(n.nodeTypesAdapter,o,{includeHelp:s!==!1}))}catch(i){return de(i,502)}}),e.registerTool("search_palette",{description:"Search the public Node-RED catalogue for modules to recommend. Returns module id, description, version, updated, link, and installed boolean (cross-checked against installed modules). Core node-red types are not in the catalogue \u2014 use list_node_types. RECOMMEND ONLY \u2014 never installs.",inputSchema:{q:_.z.string().describe("Search string (id / description / keywords; non-empty required)")}},async({q:o})=>{try{return ae(await fo(n.nodeTypesAdapter,o,{...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{}}))}catch(s){return de(s,502)}})}a(Xl,"registerReadTools");function Zl(e,t,n,r,o){let s=V(r,t.settings.httpAdminRoot);e.registerTool("submit_proposal",{description:`Submit a replace_flow proposal for owner approval (does not deploy). Preferred for small string edits: payload: { flowId, nodes: [{ op:"replace_text", node, field, find, replace, occurrence? }] } \u2014 find must be unique unless occurrence (1-based) is set; multiple replace_text ops apply in order; yields a short readable diff. Or send a full tab: payload.flow from get_flow (id, type, label?, nodes[], configs?). Inputs (all top-level, next to each other): type="replace_flow", expectedRev (required string from list_flows/get_flow rev; expected_rev alias accepted), optional title (\u226480 chars), and payload: { flowId, flow? } and/or payload.nodes / top-level nodes. Do not nest expectedRev inside payload. Secret values appear as "[REDACTED]" in reads; leave the placeholder unchanged to keep the stored secret (it is restored on submit), or replace it with a new value to change it. Payload size limit is ${K} bytes (413 if exceeded). Recent known expectedRev snapshots are accepted and auto-rebased; unknown revisions return 409 version_mismatch. Open proposals auto-rebase on revision changes; conflict blocks approval until resolved. Proposals expire after the configured TTL (default 7 days). replace_text errors: 400 text_not_found; 409 ambiguous_match with matchCount. Unknown palette types return 400 unknown_node_type with nearest matches \u2014 call list_node_types / search_palette and ask the user to install via Manage palette (never auto-install). Returns a lean proposal (id, status, title, storedTitle, flowLabel, displayName, reviewUrl, changeSummary, \u2026) by default; title may gain " in <flow>"; storedTitle is the title as saved (your title without that suffix, or the auto-generated title, which may already name the flow). Pass include_payload=true to include payload and baseFlow. When telling the user about the proposal, cite displayName and reviewUrl \u2014 not the raw id. Humans approve on the /ai-access dashboard (reviewUrl opens/scrolls to it).`,inputSchema:{type:_.z.literal("replace_flow").describe("Must be 'replace_flow'"),expectedRev:_.z.string().min(1).optional().describe("Top-level current flow revision from get_flow/list_flows (required at submit; sit next to type and payload, not inside payload)"),expected_rev:_.z.string().min(1).optional().describe("Alias for expectedRev (snake_case); prefer expectedRev"),title:_.z.string().max(80).optional().describe("Optional short human title (\u226480 chars). Auto-generated from changes when omitted."),include_payload:_.z.boolean().optional().describe("When true, include payload and baseFlow in the response (default lean)"),nodes:_.z.array(_.z.record(_.z.string(),_.z.unknown())).optional().describe("Optional node ops (set/add/remove/replace_text). Same as payload.nodes when flow is omitted."),payload:_.z.object({flowId:_.z.string().min(1).describe("Tab id to replace"),flow:_.z.object({id:_.z.string().optional(),type:_.z.string().optional().describe("Usually 'tab' (included by get_flow)"),label:_.z.string().optional(),nodes:_.z.array(_.z.record(_.z.string(),_.z.unknown())).optional().describe("Child nodes for the tab (from get_flow.nodes)"),configs:_.z.array(_.z.record(_.z.string(),_.z.unknown())).optional()}).passthrough().optional().describe("Full tab from get_flow; omit when using nodes ops"),nodes:_.z.array(_.z.record(_.z.string(),_.z.unknown())).optional().describe("Node ops applied to the live tab (prefer replace_text for small string edits)")}).describe("Replacement flow payload and/or node ops")}},async i=>{try{let d=await Tt(n.proposalStore,n.auditLog,o,{...i,expectedRev:typeof i.expectedRev=="string"&&i.expectedRev.length>0?i.expectedRev:i.expected_rev},n.flowAdapter,s,n.nodeTypesAdapter),l={...ie(d,{...s,include:Hi(i.include_payload)}),mine:!0},c=te(n.proposalStore),u=et(Qe(n.proposalStore,n.proposalStore.get(d.id),o.id));return ae({...l,...u,...pe(c)})}catch(d){return de(d)}}),Gm(e,n,o,s),Km(e,n,o,s)}a(Zl,"registerProposalTools");function Wm(e,t,n,r){let o={mine:_.z.boolean().optional().describe("true: only proposals created by this access link; false: only other links\u2019 proposals"),status:_.z.union([_.z.string(),_.z.array(_.z.string())]).optional().describe("Filter by proposal status: one value, an array, or a comma-separated string (pending, conflict, approved, rejected, failed, withdrawn, expired, superseded). An empty array or empty string means no status filter."),flowId:_.z.string().optional().describe("Filter by flow tab id (payload.flowId)"),limit:_.z.number().int().min(1).max(_n).optional().describe(`Page size (1\u2013${_n}). Omit to return all matches.`),offset:_.z.number().int().min(0).optional().describe("Skip this many filtered results (default 0)"),include_payload:_.z.boolean().optional().describe("When true, include payload and baseFlow on each proposal")},s=a(async i=>{try{let d=Hi(i.include_payload),l=await tt(t.flowAdapter),c=l?De(t.proposalStore,l).expired:te(t.proposalStore),u={};typeof i.mine=="boolean"&&(u.mine=i.mine);let p=mi(i.status);p&&(u.status=p),typeof i.flowId=="string"&&i.flowId.length>0&&(u.flowId=i.flowId),typeof i.limit=="number"&&(u.limit=i.limit),typeof i.offset=="number"&&(u.offset=i.offset);let f=En(t.proposalStore,n,r,u);return ae({proposals:f.proposals.map(m=>he(t.proposalStore,n,m,{...r,include:d},{includeConflicts:!1})),...Jt({...f,returned:f.proposals.length}),...pe(c)})}catch(d){return de(d)}},"listProposalsHandler");e.registerTool("list_proposals",{description:"List proposals visible to this access link (all proposals by default). Each item includes mine:true|false for the caller. Filter with mine, status, and/or flowId; paginate with limit/offset (response includes total, offset, hasMore, nextOffset). Invalid filters return invalid_filter. Returns lean { proposals, total, \u2026 } with title, displayName, reviewUrl, changeSummary (no payload/baseFlow unless include_payload=true). Prefer displayName + reviewUrl when referring to a proposal with the user (not the raw id).",inputSchema:o},s),e.registerTool("list_my_proposals",{description:"Alias of list_proposals. Prefer list_proposals. Lists proposals visible to this access link with optional mine/status/flowId filters and limit/offset pagination.",inputSchema:o},s)}a(Wm,"registerListProposalsTools");function Gm(e,t,n,r){Wm(e,t,n,r),e.registerTool("get_proposal",{description:"Get any proposal by id (all tokens can read). Response includes mine:true|false and, for open proposals (pending or conflict), conflictingProposals when other open work touches the same flow. baseRev aliases expectedRev; baseRevChanged is informational. rebase.state=clean|merged means mergeable; conflict blocks approval. Lean by default (no payload/baseFlow); pass include_payload=true for full bodies. Cite displayName + reviewUrl when talking to the user.",inputSchema:{id:_.z.string().min(1).describe("Proposal id (prop-\u2026)"),include_payload:_.z.boolean().optional().describe("When true, include payload and baseFlow")}},async({id:o,include_payload:s})=>{try{let i=await tt(t.flowAdapter),d=ut(t.proposalStore,n,o,r);return ae(he(t.proposalStore,n,d,{...r,include:Hi(s)},{...i?{live:i}:{}}))}catch(i){return de(i)}})}a(Gm,"registerProposalQueryTools");function Km(e,t,n,r){e.registerTool("get_proposal_diff",{description:"Diff any proposal against its stored baseFlow snapshot (what changed). Optional format=summary|full (default summary). Open proposals include rebase and liveChanges; conflict blocks approval, otherwise vsCurrent shows the merged deployment.",inputSchema:{id:_.z.string().min(1).describe("Proposal id (prop-\u2026)"),format:_.z.enum(["summary","full"]).optional().describe("Diff payload format (default summary)")}},async({id:o,format:s})=>{try{let i=ut(t.proposalStore,n,o),d=Mn(s??"summary");if(!d.ok)return de(new k("Invalid diff format","invalid_diff_format",{details:{hint:"Valid formats: summary, full (default summary)."}}));let l=Dn(await Bn(i,t.flowAdapter,t.proposalStore.get(o)),d.format);return ae(l)}catch(i){return de(i)}}),e.registerTool("resolve_proposal_conflict",{description:"Resolve your own open proposal. Different node/tab fields auto-merge; only fields changed on both sides conflict (x/y never conflict). Choose one current conflict using nodeId + choice, preserving all non-conflicting fields. Alternatively replace the proposal using expectedRev + payload: nodes ops apply to the flow snapshot at expectedRev, NOT the stored proposal or merged result; include every intended proposal edit. Read get_proposal_diff for conflicts and get_flow for a fresh rev. These two modes cannot be mixed.",inputSchema:{id:_.z.string().min(1).describe("Your open proposal id (prop-\u2026)"),nodeId:_.z.string().min(1).optional().describe("Conflict nodeId from rebase.conflicts; use the flow id for tab/env conflicts. Required with choice."),choice:_.z.enum(["live","proposed"]).optional().describe("For this conflict, keep live or proposed values of overlapping fields only. Disjoint edits survive. Use with nodeId, without expectedRev/title/payload."),expectedRev:_.z.string().min(1).optional().describe("Revision of the flow snapshot on which replacement payload ops run. Required with payload; normally the current get_flow rev."),title:_.z.string().max(80).optional().describe("Optional replacement title (body replacement mode only)"),payload:_.z.object({flowId:_.z.string().min(1).describe("Target flow id; must remain the proposal\u2019s flow id"),flow:_.z.record(_.z.string(),_.z.unknown()).optional().describe("Full replacement tab based on expectedRev; preserve all desired edits. Use either flow or nodes."),nodes:_.z.array(_.z.record(_.z.string(),_.z.unknown())).optional().describe("set/add/remove/replace_text operations applied to the flow at expectedRev, not to the stored proposal. Env merges by unique name and credentials by key; same-field edits conflict.")}).optional().describe("Replacement body; required with expectedRev, omitted with nodeId + choice")}},async({id:o,...s})=>{try{let i=await Co(t.proposalStore,t.auditLog,n,o,s,t.flowAdapter,r,t.nodeTypesAdapter);return ae(he(t.proposalStore,n,i,r))}catch(i){return de(i)}}),e.registerTool("withdraw_proposal",{description:"Withdraw one of YOUR open (pending or conflict) proposals (status becomes withdrawn, not rejected). Optional reason. Only the creating access link can withdraw via MCP; other tokens get 403. If it is lost, a full admin session can recover via POST /proposals/{id}/withdraw. Cite displayName + reviewUrl when telling the user.",inputSchema:{id:_.z.string().min(1).describe("Proposal id (prop-\u2026) owned by this access link"),reason:_.z.string().optional().describe("Optional withdraw reason")}},({id:o,reason:s})=>{try{let i=In(t.proposalStore,t.auditLog,n,o,s,r);return ae(he(t.proposalStore,n,i,r))}catch(i){return de(i)}})}a(Km,"registerProposalActionTools");function Jm(e,t,n){let r=new Di.McpServer({name:"node-red-ai-access",version:Vl()});Yl(r);let o=n.accessLink;if(!o)throw new Error("Access link required");return Xl(r,e,t,n),Zl(r,e,t,n,o),r}a(Jm,"createAiMcpServer");function Vm(e){let t=Mi(e);if(t)return t;let n=e.auth?.clientId??"",r=typeof e.auth?.extra?.user=="string"?e.auth.extra.user:"owner";return{kind:"access-link",linkId:so(n,r),label:"oauth"}}a(Vm,"mcpAuditActor");function ji(e,t){return async(n,r,o)=>{try{if(!n.accessLink&&!n.auth){r.setHeader("WWW-Authenticate",fe),r.status(401).json({error:Ie,code:at});return}let s={...t,flowAdapter:At(t.flowAdapter)},i=n.accessLink?Jm(e,s,n):Ym(e,s,n),d=new Jl.StreamableHTTPServerTransport({enableJsonResponse:!0});await i.connect(d),await Wl(t.auditLog,Vm(n),Gl(n.body),()=>d.handleRequest(n,r,n.body)),r.on("close",()=>{d.close(),i.close()})}catch(s){if(!r.headersSent){B(r,s);return}o(s instanceof Error?s:new Error(String(s)))}}}a(ji,"mcpRpcPostHandler");function Ym(e,t,n){let r=new Di.McpServer({name:"node-red-ai-access",version:Vl()});if(Yl(r),Xl(r,e,t,n),(n.auth?.scopes??[]).includes("propose")){let s=typeof n.auth?.extra?.user=="string"?n.auth.extra.user:"owner",i=n.auth?.clientId??"",l={id:so(i,s),kind:"mcp",createdAt:new Date().toISOString(),expiresAt:new Date((n.auth?.expiresAt??0)*1e3).toISOString(),createdBy:s,oauthClientId:i,oauthUser:s};Zl(r,e,t,n,l)}return r}a(Ym,"createOAuthMcpServer");function Nr(e,t){t.status(405).set("Allow","POST").json({jsonrpc:"2.0",error:{code:-32e3,message:"Method not allowed. Use POST for Streamable HTTP."},id:null})}a(Nr,"mcpRpcMethodNotAllowed");var Fr=require("crypto"),Wn=require("fs"),zi=require("path"),Z=require("@modelcontextprotocol/sdk/server/auth/errors.js"),rc=require("@modelcontextprotocol/sdk/server/auth/handlers/authorize.js"),Wi=require("@modelcontextprotocol/sdk/server/auth/handlers/metadata.js"),oc=require("@modelcontextprotocol/sdk/server/auth/handlers/register.js"),sc=require("@modelcontextprotocol/sdk/server/auth/handlers/revoke.js"),ic=require("@modelcontextprotocol/sdk/server/auth/handlers/token.js"),ac=require("@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js"),dc=require("@modelcontextprotocol/sdk/server/auth/router.js"),lc=Vn(require("express"),1);oe();ze();function st(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t]??t)}a(st,"esc");var Ql={read:'<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5c-5 0-9 4.5-10 7 1 2.5 5 7 10 7s9-4.5 10-7c-1-2.5-5-7-10-7Zm0 11.5a4.5 4.5 0 1 1 0-9 4.5 4.5 0 0 1 0 9Zm0-2.3a2.2 2.2 0 1 0 0-4.4 2.2 2.2 0 0 0 0 4.4Z"/></svg>',propose:'<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 17.2V20h2.8l8.3-8.3-2.8-2.8L4 17.2Zm13.7-7.6a1 1 0 0 0 0-1.4l-1.9-1.9a1 1 0 0 0-1.4 0l-1.4 1.4 2.8 2.8 1.9-1.9Z"/></svg>'},Xm={read:["Read your flows","View flows, nodes, node types and AI Access logs."],propose:["Propose flow changes","Submit change proposals. Nothing is deployed until you approve it in Node-RED."]},Ui='<svg class="logo" viewBox="0 0 48 48" aria-hidden="true"><rect width="48" height="48" rx="11" fill="#8f0000"/><path d="M11 15h9a4 4 0 0 1 4 4v10a4 4 0 0 0 4 4h9" fill="none" stroke="#fff" stroke-width="3" stroke-linecap="round"/><rect x="6" y="11" width="9" height="8" rx="2.5" fill="#fff"/><rect x="33" y="29" width="9" height="8" rx="2.5" fill="#fff"/><circle cx="37.5" cy="16" r="5" fill="none" stroke="#fff" stroke-width="2.4"/><path d="M37.5 13.5v5M35 16h5" stroke="#fff" stroke-width="2.2" stroke-linecap="round"/></svg>',Zm=`
991
991
  :root{color-scheme:light;--bg:#f3f4f6;--card:#fff;--text:#111827;--muted:#4b5563;--line:#e5e7eb;--soft:#f9fafb;--brand:#8f0000;--brand-2:#b91c1c;--ok:#0f766e;--warn-bg:#fff7ed;--warn:#9a3412;--focus:#2563eb}
992
992
  *{box-sizing:border-box}
993
993
  body{margin:0;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px 16px;background:var(--bg);color:var(--text);font:15px/1.5 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif}
@@ -1023,7 +1023,7 @@ form.login input{font:inherit;padding:9px 11px;border-radius:10px;border:1px sol
1023
1023
  a{color:var(--focus)}
1024
1024
  [hidden]{display:none!important}
1025
1025
  @media (max-width:420px){.actions{flex-direction:column-reverse}.secondary{flex:1}}
1026
- `;function Jl(e){let t=e.scopes.map(n=>{let[r,o]=Km[n]??[n,""];return`<li data-scope="${rt(n)}">${Kl[n]??Kl.read}<div><b>${rt(r)}</b><span>${rt(o)}</span></div></li>`}).join("");return`<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="referrer" content="no-referrer"><title>Authorize MCP client \xB7 Node-RED AI Access</title><style>${Jm}</style></head><body><main class="card" data-request="${rt(e.requestId)}" data-token-key="${rt(e.tokenKey)}" data-admin-root="${rt(e.adminRoot)}"><div class="head">${Fi}<div class="brand"><b>Node-RED AI Access</b><span>Authorization request</span></div></div><div class="body"><h1><span class="app">${rt(e.redirectHost)}</span> wants to access your Node-RED</h1><p class="sub">Access tokens will be sent to <span class="host">${rt(e.redirectHost)}</span>. App name (provided by the client, unverified): <b>${rt(e.clientName)}</b></p>${e.loopbackRedirect?'<p class="warn">Warning: this redirect address is reachable only on this machine.</p>':""}<ul class="perms" aria-label="Requested permissions">${t}</ul><p id="status" class="status" role="status" aria-live="polite">Checking your Node-RED session\u2026</p><form id="login" class="login" hidden autocomplete="on"><label>Username<input id="username" name="username" autocomplete="username" required></label><label>Password<input id="password" name="password" type="password" autocomplete="current-password" required></label><div class="actions"><button id="login-submit" class="primary" type="submit">Sign in</button></div></form><p id="grant" class="sub" hidden></p><p id="user" class="user" hidden><span class="avatar" id="avatar" aria-hidden="true"></span><span>Signed in as <b id="username-label"></b></span></p><div class="actions"><button id="deny" class="secondary" type="button" hidden>Deny</button><button id="allow" class="primary" type="button" hidden>Allow access</button><button id="retry" class="secondary" type="button" hidden>Retry</button></div></div><div class="foot">You can revoke this app at any time from the AI Access dashboard (OAuth tab) in Node-RED.</div></main><script nonce="${rt(e.nonce)}">${e.script}</script></body></html>`}a(Jl,"renderConsentPage");var os=`
1026
+ `;function ec(e){let t=e.scopes.map(n=>{let[r,o]=Xm[n]??[n,""];return`<li data-scope="${st(n)}">${Ql[n]??Ql.read}<div><b>${st(r)}</b><span>${st(o)}</span></div></li>`}).join("");return`<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="referrer" content="no-referrer"><title>Authorize MCP client \xB7 Node-RED AI Access</title><style>${Zm}</style></head><body><main class="card" data-request="${st(e.requestId)}" data-token-key="${st(e.tokenKey)}" data-admin-root="${st(e.adminRoot)}"><div class="head">${Ui}<div class="brand"><b>Node-RED AI Access</b><span>Authorization request</span></div></div><div class="body"><h1><span class="app">${st(e.redirectHost)}</span> wants to access your Node-RED</h1><p class="sub">Access tokens will be sent to <span class="host">${st(e.redirectHost)}</span>. App name (provided by the client, unverified): <b>${st(e.clientName)}</b></p>${e.loopbackRedirect?'<p class="warn">Warning: this redirect address is reachable only on this machine.</p>':""}<ul class="perms" aria-label="Requested permissions">${t}</ul><p id="status" class="status" role="status" aria-live="polite">Checking your Node-RED session\u2026</p><form id="login" class="login" hidden autocomplete="on"><label>Username<input id="username" name="username" autocomplete="username" required></label><label>Password<input id="password" name="password" type="password" autocomplete="current-password" required></label><div class="actions"><button id="login-submit" class="primary" type="submit">Sign in</button></div></form><p id="grant" class="sub" hidden></p><p id="user" class="user" hidden><span class="avatar" id="avatar" aria-hidden="true"></span><span>Signed in as <b id="username-label"></b></span></p><div class="actions"><button id="deny" class="secondary" type="button" hidden>Deny</button><button id="allow" class="primary" type="button" hidden>Allow access</button><button id="retry" class="secondary" type="button" hidden>Retry</button></div></div><div class="foot">You can revoke this app at any time from the AI Access dashboard (OAuth tab) in Node-RED.</div></main><script nonce="${st(e.nonce)}">${e.script}</script></body></html>`}a(ec,"renderConsentPage");var as=`
1027
1027
  function nrLoginType(adminRootHref) {
1028
1028
  return fetch(new URL('auth/login', adminRootHref).href, {
1029
1029
  headers: { 'Node-RED-API-Version': 'v2' }, credentials: 'same-origin'
@@ -1049,8 +1049,8 @@ a{color:var(--focus)}
1049
1049
  return { ok: true };
1050
1050
  });
1051
1051
  }
1052
- `;var Vl=`(function () {
1053
- ${os}
1052
+ `;var tc=`(function () {
1053
+ ${as}
1054
1054
  var main = document.querySelector('main');
1055
1055
  var id = main.getAttribute('data-request');
1056
1056
  var key = main.getAttribute('data-token-key');
@@ -1174,7 +1174,7 @@ ${os}
1174
1174
  document.addEventListener('visibilitychange', function () { if (document.visibilityState === 'visible') autoRetry(false); });
1175
1175
  window.addEventListener('focus', function () { autoRetry(false); });
1176
1176
  load();
1177
- })();`;ke();var Lt=a(e=>(0,Ir.createHash)("sha256").update(e).digest("hex"),"hash"),ln=a(()=>(0,Ir.randomBytes)(32).toString("base64url"),"secret"),Vm=36e5,Ym=25,Xm=5,Zm=500,Yl=a(()=>({clients:[],codes:[],tokens:[]}),"emptyDb"),ss=class{constructor(t,n){this.maxClients=n;this.path=(0,Mi.join)(t.root,"oauth.json");try{let r=(0,Hn.existsSync)(this.path)?JSON.parse((0,Hn.readFileSync)(this.path,"utf8")):Yl();if(!r||typeof r!="object"||!("clients"in r)||!("codes"in r)||!("tokens"in r)||!Array.isArray(r.clients)||!Array.isArray(r.codes)||!Array.isArray(r.tokens))throw new Error("Invalid OAuth store");this.db=r}catch{this.db=Yl()}(0,Hn.mkdirSync)(t.root,{recursive:!0,mode:448}),this.prune()}static{a(this,"OAuthStore")}db;path;save(){cn((0,Mi.join)(this.path,".."),this.path,ft(this.db,8e6,{kind:"oauth",pretty:!0}))}prune(t=Date.now()){let n=this.db.clients.length+this.db.codes.length+this.db.tokens.length,r=new Set(this.db.clients.filter(o=>o.approved?(o.lastUsedAt??(o.client_id_issued_at??0)*1e3)>t-30*864e5:(o.client_id_issued_at??0)*1e3>t-Vm).map(o=>o.client_id));this.db.clients=this.db.clients.filter(o=>r.has(o.client_id)),this.db.codes=this.db.codes.filter(o=>o.expires>t&&r.has(o.clientId)),this.db.tokens=this.db.tokens.filter(o=>r.has(o.clientId)&&o.expires>t),this.db.clients.length+this.db.codes.length+this.db.tokens.length!==n&&this.save()}getClient(t){return this.prune(),this.db.clients.find(n=>n.client_id===t)}registerClient(t){if(this.prune(),this.approvedCount()>=this.maxClients())throw new Q.InvalidClientMetadataError("Maximum active OAuth clients reached");if(this.db.clients.length-this.approvedCount()>=Ym)throw new Q.InvalidClientMetadataError("Too many OAuth clients awaiting consent");if(!Array.isArray(t.redirect_uris)||!t.redirect_uris.length||t.redirect_uris.some(r=>{try{let o=new URL(r);return!!o.username||!!o.password||!!o.hash||o.protocol!=="https:"&&!(o.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(o.hostname))}catch{return!0}}))throw new Q.InvalidClientMetadataError("Redirect URIs must use HTTPS or loopback HTTP");if(t.token_endpoint_auth_method&&t.token_endpoint_auth_method!=="none")throw new Q.InvalidClientMetadataError("Only public clients are supported");let n={...t,client_secret:void 0,client_secret_expires_at:void 0,client_id:ln(),client_id_issued_at:Math.floor(Date.now()/1e3),token_endpoint_auth_method:"none",lastUsedAt:Date.now()};return this.db.clients.push(n),this.save(),n}approvedCount(){return this.db.clients.filter(t=>t.approved).length}approve(t){let n=this.db.clients.find(r=>r.client_id===t);if(!(!n||n.approved)){if(this.approvedCount()>=this.maxClients())throw new Q.InvalidClientMetadataError("Maximum active OAuth clients reached");n.approved=!0,this.save()}}get data(){return this.db}persist(){this.touchTimer&&(clearTimeout(this.touchTimer),this.touchTimer=void 0),this.save()}touchTimer;touch(){this.touchTimer||(this.touchTimer=setTimeout(()=>{this.touchTimer=void 0,this.save()},5e3),this.touchTimer.unref?.())}revokeClient(t){let n=this.db.clients.length;return this.db.clients=this.db.clients.filter(r=>r.client_id!==t),this.db.tokens=this.db.tokens.filter(r=>r.clientId!==t),this.db.codes=this.db.codes.filter(r=>r.clientId!==t),this.save(),this.db.clients.length!==n}};function ji(e){switch(e.source){case"http-in":return`Route ${e.path} is already used by http-in node '${e.nodeName??e.nodeId??"unnamed"}'`;case"httpStatic":return`File ${e.path} is served by httpStatic`;case"middleware":return`Path prefix is already handled by mounted middleware (${e.layer??"unknown"})`;default:return`Route ${e.path} is already registered by another handler (${e.layer??"route"})`}}a(ji,"describeOAuthRouteCollision");var Ui=a((e,t)=>e.toLowerCase().startsWith(t.toLowerCase()),"startsWithCi");function Oi(e){return e===!1?void 0:`/${typeof e=="string"?e:"/"}`.replace(/\/+/g,"/").replace(/\/$/,"")}a(Oi,"normalizeRoot");function $i(e,t){if(e!==void 0){if(!e)return t;if(Ui(t,`${e}/`))return t.slice(e.length)}}a($i,"relativePrefix");function Qm(e){let t=new Map;for(let r of e)r.id&&t.set(r.id,r);let n=a((r,o=new Set)=>{if(!r)return!0;let s=t.get(r);return s?s.type==="tab"?!s.disabled&&!s.d:s.type!=="subflow"||o.has(r)?!1:(o.add(r),e.some(i=>i.type===`subflow:${r}`&&!i.d&&n(i.z,o))):!0},"live");return n}a(Qm,"makeLiveCheck");function eg(e,t){if(!e||!t.length)return[];let n=[];try{e(s=>n.push(s))}catch{return[]}let r=Qm(n),o=[];for(let s of n){if(s.type!=="http in"||typeof s.url!="string"||s.d)continue;let i=s.url.startsWith("/")?s.url:`/${s.url}`;!t.some(d=>Ui(i,d))||!r(s.z)||o.push({path:i,source:"http-in",nodeId:s.id,nodeName:s.name?s.name:void 0,flowId:s.z})}return o}a(eg,"findHttpInCollisions");var tg=new Set(["bound dispatch","handle"]),ng=["authorize","token","register","revoke"],rg=["authorization-server","protected-resource"];function og(e){let t=e.length;for(;t>0&&e.charCodeAt(t-1)===47;)t--;return e.slice(0,t)}a(og,"trimTrailingSlashes");function sg(e){let t=new Set;return e.filter(n=>{let r=`${n.source}\0${n.path.toLowerCase()}`;return t.has(r)?!1:(t.add(r),!0)})}a(sg,"dedupe");function oc(e){let t=Oi(e.httpAdminRoot),n=e.httpNode?Oi(e.httpNodeRoot):void 0,r=`${t??""}/mcp/oauth/`,o=e.metadataFallback?`${t??""}/mcp/.well-known/oauth-`:`${n??""}/.well-known/oauth-`,s=a(p=>{let f=$i(t,p),h=$i(n,p),m=h?eg(e.eachNode,[h]):[],g=new Set(m.map(x=>x.path.toLowerCase())),y=h?Hi(e.httpNode,[h]).filter(x=>!(x.source==="route"&&tg.has(x.layer??"")&&g.has(x.path.toLowerCase()))):[],A=f?Hi(e.httpAdmin,[f]):[];return sg([...A,...y,...m])},"scan"),i=s(r),d=s(o),l=e.fileExists??(()=>!1),c=(e.staticDirs??[]).map(p=>typeof p=="string"?{path:p,root:e.httpStaticRoot}:p),u=a((p,f)=>{for(let h of c){let m=Oi(h.root),g=og(h.path);for(let y of p){let A=$i(m,y);!A||!l(`${g}${A}`)||f.push({path:y,source:"httpStatic",file:`${g}${A}`})}}},"checkStatic");return u(ng.map(p=>`${r}${p}`),i),u(rg.map(p=>`${o}${p}`),d),{authorization:i,metadata:d}}a(oc,"scanOAuthRouteCollisions");function ig(e){let t=a(n=>{try{return e?.[n]}catch{return}},"read");return t("_router")?.stack??t("router")?.stack??[]}a(ig,"routerStack");function ag(e){let t=e.route?.path;return t?(Array.isArray(t)?t:[t]).map(String):[]}a(ag,"layerPaths");function dg(e,t){if(e.name==="router")return!1;let n=e.regexp,r=e.matchers??(n?[s=>n.test(s)]:[]);if(!r.length)return!1;let o=a(s=>r.some(i=>!!i(s)),"matches");return o("/unrelated-ai-access-collision-probe")?!1:t.some(s=>o(`${s}token`)||o(`${s}mcp`))}a(dg,"middlewareHits");function Hi(e,t=["/.well-known/oauth-","/mcp/oauth/"]){let n=[];for(let r of ig(e)){if(r.handle?.oauthOwned)continue;let o=ag(r);for(let s of o)t.some(i=>Ui(s,i))&&n.push({path:s,source:"route",layer:r.name??"route"});!o.length&&dg(r,t)&&n.push({path:t.join(", "),source:"middleware",layer:r.name??"route"}),r.handle?.stack&&n.push(...Hi({router:{stack:r.handle.stack}},t))}return n}a(Hi,"detectOAuthRouteCollisionDetails");var is=class{constructor(t,n,r,o,s=()=>0){this.clientsStore=t;this.resource=n;this.accessTtl=r;this.refreshTtl=o;this.refreshMax=s}static{a(this,"LocalOAuthProvider")}skipLocalPkceValidation=!1;inflight=new Map;async authorize(t,n,r){throw new Q.InvalidGrantError("Admin consent is required")}async challengeForAuthorizationCode(t,n){let r=this.findCode(t,n);return this.clientsStore.data.codes=this.clientsStore.data.codes.filter(o=>o!==r),this.clientsStore.persist(),this.inflight.set(r.hash,r),setTimeout(()=>this.inflight.delete(r.hash),3e4).unref?.(),r.challenge}async exchangeAuthorizationCode(t,n,r,o,s){let i=this.inflight.get(Lt(n))??this.findCode(t,n);if(this.inflight.delete(i.hash),this.clientsStore.data.codes=this.clientsStore.data.codes.filter(d=>d!==i),this.clientsStore.persist(),i.clientId!==t.client_id||i.expires<=Date.now()||o!==i.redirectUri||s&&Nt(s.href)!==i.resource)throw new Q.InvalidGrantError("Invalid authorization code");return this.issue(t.client_id,i.scopes,i.resource,i.user)}async exchangeRefreshToken(t,n,r,o){let s=this.clientsStore.data.tokens.find(l=>l.hash===Lt(n)&&l.type==="refresh"&&l.clientId===t.client_id);if(!s||s.expires<=Date.now())throw new Q.InvalidGrantError("Invalid refresh token");if(s.rotated)throw this.clientsStore.data.tokens=this.clientsStore.data.tokens.filter(l=>l.family!==s.family),this.clientsStore.persist(),new Q.InvalidGrantError("Refresh token reuse detected");let i=this.refreshMax()*1e3,d=s.familyStart??Date.now();if(i>0&&d+i<=Date.now())throw new Q.InvalidGrantError("Refresh token family reached its maximum lifetime");if(o&&Nt(o.href)!==s.resource)throw new Q.InvalidGrantError("Resource mismatch");if(r?.some(l=>!s.scopes.includes(l)))throw new Q.InvalidGrantError("Invalid scope");return s.rotated=!0,this.issue(t.client_id,r??s.scopes,s.resource,s.user,s.family,d)}async verifyAccessToken(t){let n=this.clientsStore.data.tokens.find(o=>o.hash===Lt(t)&&o.type==="access"&&o.expires>Date.now());if(!n||n.resource!==Nt(this.resource()))throw new Q.InvalidTokenError("Invalid access token");let r=this.clientsStore.data.clients.find(o=>o.client_id===n.clientId);if(!r)throw new Q.InvalidTokenError("Invalid client");return r.lastUsedAt=Date.now(),this.clientsStore.touch(),{token:t,clientId:n.clientId,scopes:n.scopes,expiresAt:Math.floor(n.expires/1e3),resource:new URL(n.resource),extra:{user:n.user}}}async revokeToken(t,n){let r=this.clientsStore.data.tokens.find(o=>o.hash===Lt(n.token)&&o.clientId===t.client_id);r&&(this.clientsStore.data.tokens=this.clientsStore.data.tokens.filter(o=>o.family!==r.family)),this.clientsStore.persist()}createCode(t,n,r){let o=ln();return this.clientsStore.data.codes.push({hash:Lt(o),clientId:t.client_id,challenge:n.codeChallenge,redirectUri:n.redirectUri,scopes:n.scopes??["read"],resource:this.validateResource(n.resource),expires:Date.now()+6e4,user:r}),this.clientsStore.persist(),o}validateResource(t){let n=Nt(this.resource());if(t&&Nt(t.href)!==n)throw new Q.InvalidGrantError("Resource mismatch");return n}findCode(t,n){let r=this.clientsStore.data.codes.find(o=>o.hash===Lt(n)&&o.clientId===t.client_id);if(!r||r.expires<=Date.now())throw new Q.InvalidGrantError("Invalid or expired authorization code");return r}issue(t,n,r,o,s=ln(),i=Date.now()){let d=ln(),l=ln(),c=Date.now(),u=this.refreshMax()*1e3,p=Math.min(c+this.refreshTtl()*1e3,u>0?i+u:1/0),f=this.clientsStore.data,h=f.clients.find(m=>m.client_id===t);return h&&(h.lastUsedAt=c),f.tokens.push({hash:Lt(d),type:"access",clientId:t,family:s,scopes:n,resource:r,expires:c+this.accessTtl()*1e3,user:o,rotated:!1},{hash:Lt(l),type:"refresh",clientId:t,family:s,scopes:n,resource:r,expires:p,user:o,rotated:!1,familyStart:i}),this.clientsStore.persist(),{access_token:d,token_type:"Bearer",expires_in:this.accessTtl(),refresh_token:l,scope:n.join(" ")}}};function Nt(e){let t=new URL(e);if(!["http:","https:"].includes(t.protocol)||t.username||t.password||t.search||t.hash)throw new Error("Invalid resource URL");return`${t.protocol}//${t.host}${t.pathname.replace(/\/+$/,"")}`}a(Nt,"canonicalResource");var Dn=["read","propose"];function lg(e){let t=(e??"/").split("/").filter(Boolean).join("/");return t?`/${t}/`:"/"}a(lg,"consentAdminRoot");var cg=new Set(["localhost","127.0.0.1","[::1]"]);function dn(e){return typeof e=="string"?e:""}a(dn,"value");function sc(e,t,n,r){let o=a(T=>`${n().replace(/\/$/,"")}${T}`,"abs"),s=a((T,k,_,E)=>{let v=k.url;k.url="/",T(k,_,S=>{k.url=v,E(S)})},"atRoot"),i=new Map,d=a(()=>{let T=Date.now();for(let[k,_]of i)_.expires<=T&&i.delete(k)},"sweep"),l=a(T=>{d();let k=[...i].filter(([,_])=>_.clientId===T);for(;k.length>=Xm;)i.delete(k.shift()[0]);return i.size<Zm},"reservePending"),c=a((T,k)=>{T.setHeader("Cache-Control","no-store"),T.setHeader("X-Frame-Options","DENY"),T.setHeader("Referrer-Policy","no-referrer"),T.setHeader("X-Content-Type-Options","nosniff"),T.setHeader("Content-Security-Policy",`default-src 'none'; ${k?`script-src 'nonce-${k}'; `:""}style-src 'unsafe-inline'; connect-src 'self'; form-action 'none'; base-uri 'none'; frame-ancestors 'none'`)},"securityHeaders"),u=Vr(e.settings.httpAdminRoot),p=a(T=>{if(!oe(e.settings))return[...Dn];let k=mt(T);return k==="*"?[...Dn]:k==="read"?["read"]:[]},"grantableFor"),f=a(T=>{let k=T.get("sec-fetch-site");if(k&&k!=="same-origin")return!1;let _=T.get("origin");if(!_)return!0;let E=new Set;try{E.add(new URL(n()).origin)}catch{}let v=T.get("host");return v&&(E.add(`http://${v}`),E.add(`https://${v}`)),E.has(_)},"sameOrigin"),h=a((T,k)=>{d();let _=T.query,E=t.clientsStore.getClient(dn(_.client_id));if(!E){k.status(400).send("Unknown OAuth client");return}let v=dn(_.redirect_uri)||(E.redirect_uris.length===1?E.redirect_uris[0]:"");if(!E.redirect_uris.some(ee=>(0,Xl.redirectUriMatches)(v,ee))){k.status(400).send("Unregistered redirect URI");return}if(_.response_type!=="code"||_.code_challenge_method!=="S256"||!/^[A-Za-z0-9_-]{43,128}$/.test(dn(_.code_challenge))){k.status(400).send("Authorization code with PKCE S256 is required");return}let S=[...new Set((dn(_.scope)||"read").split(" ").filter(Boolean))];if(S.some(ee=>!Dn.includes(ee))){k.status(400).send("Unsupported scope");return}let N=dn(_.resource)||r(),w;try{w=Nt(N)}catch{k.status(400).send("Invalid resource");return}if(w!==Nt(r())){k.status(400).send("Resource mismatch");return}if(!l(E.client_id)){k.status(429).send("Too many pending authorization requests; try again later");return}let F=ln();i.set(F,{clientId:E.client_id,redirect:v,challenge:dn(_.code_challenge),scopes:S,resource:N,state:dn(_.state),csrf:ln(),expires:Date.now()+10*6e4});let I=new URL(v).host,C=(0,Ir.randomBytes)(16).toString("base64");c(k,C),k.status(200).type("html").send(Jl({requestId:F,tokenKey:u,adminRoot:lg(e.settings.httpAdminRoot),nonce:C,redirectHost:I,loopbackRedirect:cg.has(new URL(v).hostname),clientName:E.client_name||"Unnamed client",scopes:S,script:Vl}))},"authorizePage"),m=a((T,k,_)=>{c(k),rc.default.json({limit:"4kb"})(T,k,()=>Ie(e,"api")(T,k,()=>{try{d();let E=/^\/mcp\/oauth\/consent\/([A-Za-z0-9_-]+)\/?$/.exec(T.path)?.[1]??"",v=i.get(E),S=v&&t.clientsStore.getClient(v.clientId);if(!v||!S){k.status(404).json({error:"Consent request expired or unknown"});return}let N=oe(e.settings)&&T.user?.username||"owner",w=v.scopes.filter(C=>p(T).includes(C));if(!w.length){k.status(403).json({error:"Your Node-RED permissions do not allow granting the requested scopes",grantable:w});return}if(T.method==="GET"){v.approver=N,k.json({client_name:S.client_name??null,redirect_host:new URL(v.redirect).host,requested:v.scopes,grantable:w,user:N,csrf:v.csrf});return}if(T.method!=="POST"){k.status(405).end();return}let F=T.body??{};if(!f(T)||F.csrf!==v.csrf||v.approver!==N){k.status(403).json({error:"Consent request verification failed"});return}i.delete(E);let I=new URL(v.redirect);if(F.decision!=="allow")I.searchParams.set("error","access_denied");else{try{t.clientsStore.approve(S.client_id)}catch{k.status(409).json({error:"Maximum active OAuth clients reached"});return}let C=t.createCode(S,{redirectUri:v.redirect,codeChallenge:v.challenge,scopes:w,resource:new URL(v.resource)},N);I.searchParams.set("code",C)}v.state&&I.searchParams.set("state",v.state),k.json({redirect:I.href})}catch(E){_(E)}}))},"consentApi"),g=(0,Zl.clientRegistrationHandler)({clientsStore:t.clientsStore,rateLimit:{windowMs:36e5,max:10,standardHeaders:!0,legacyHeaders:!1}}),y=(0,ec.tokenHandler)({provider:t}),A=(0,Ql.revocationHandler)({provider:t}),x,$="";return{server:a((T,k,_)=>{let E=T.path,v=E.replace(/\/+$/,"").toLowerCase();if(v==="/mcp/oauth/authorize"){T.method==="GET"?h(T,k,_):k.status(405).set("Allow","GET").end();return}if(v.startsWith("/mcp/oauth/consent/")){m(T,k,_);return}if(!["/mcp/oauth/token","/mcp/oauth/register","/mcp/oauth/revoke"].includes(v)){_();return}let S=n();if(S.startsWith("https://")||/^http:\/\/(localhost|127\.0\.0\.1)(:|\/|$)/.test(S)){if($!==S)try{x=(0,nc.mcpAuthRouter)({provider:t,issuerUrl:new URL(S),baseUrl:new URL(`${S.replace(/\/$/,"")}/mcp/oauth/`),resourceServerUrl:new URL(r()),scopesSupported:Dn,clientRegistrationOptions:{rateLimit:{windowMs:36e5,max:10}}}),$=S}catch{x=void 0}if(x){let N=T.url;T.url=T.url.slice(10)||"/",x(T,k,w=>{T.url=N,_(w)});return}}if(E==="/mcp/oauth/token"){s(y,T,k,_);return}if(E==="/mcp/oauth/register"){s(g,T,k,_);return}if(E==="/mcp/oauth/revoke"){s(A,T,k,_);return}_()},"server"),metadata:a((T,k,_)=>{let E=T.path;if(E==="/.well-known/oauth-authorization-server"){s((0,Di.metadataHandler)({issuer:n(),authorization_endpoint:o("/mcp/oauth/authorize"),token_endpoint:o("/mcp/oauth/token"),registration_endpoint:o("/mcp/oauth/register"),revocation_endpoint:o("/mcp/oauth/revoke"),response_types_supported:["code"],code_challenge_methods_supported:["S256"],grant_types_supported:["authorization_code","refresh_token"],token_endpoint_auth_methods_supported:["none"],scopes_supported:Dn}),T,k,_);return}if(E==="/.well-known/oauth-protected-resource/mcp"||E==="/.well-known/oauth-protected-resource"){s((0,Di.metadataHandler)({resource:Nt(r()),authorization_servers:[n()],scopes_supported:Dn}),T,k,_);return}_()},"metadata"),auth:a((T,k,_)=>{try{let E=k.set.bind(k);k.set=((v,S)=>(v==="WWW-Authenticate"&&typeof S=="string"&&(S=S.replace('scope="read"','scope="read propose"')),typeof v=="string"?E(v,S??""):E(v))),(0,tc.requireBearerAuth)({verifier:t,requiredScopes:["read"],resourceMetadataUrl:o(e.settings.httpNodeRoot===!1||!e.httpNode?"/mcp/.well-known/oauth-protected-resource/mcp":"/.well-known/oauth-protected-resource/mcp"),expectedResource:new URL(r())})(T,k,v=>{k.set=E,_(v)})}catch(E){_(E)}},"auth")}}a(sc,"makeOAuthHandlers");var Lr=require("fs"),jn=require("path"),ic=require("url"),ac=require("yaml"),hg={};function ug(){return typeof __dirname<"u"?__dirname:(0,jn.dirname)((0,ic.fileURLToPath)(hg.url))}a(ug,"moduleDir");function dc(){let e=ug(),t=[(0,jn.join)(e,"..","docs","api","openapi.yaml"),(0,jn.join)(e,"..","..","docs","api","openapi.yaml"),(0,jn.join)(process.cwd(),"docs","api","openapi.yaml")];for(let n of t)if((0,Lr.existsSync)(n))return n;return null}a(dc,"resolveOpenApiYamlPath");function pg(){let e=dc();if(!e)throw new Error("OpenAPI specification file was not found");return(0,Lr.readFileSync)(e,"utf8")}a(pg,"loadOpenApiYaml");function lc(){return(e,t)=>{try{let n=pg();t.status(200).type("application/yaml").send(n)}catch(n){let r=n instanceof Error?n.message:String(n);t.status(500).json({error:r})}}}a(lc,"openApiYamlHandler");var Bi;function fg(){let e=dc();if(!e)throw new Error("OpenAPI specification file was not found");return Bi?.path!==e&&(Bi={path:e,json:JSON.stringify((0,ac.parse)((0,Lr.readFileSync)(e,"utf8")))}),Bi.json}a(fg,"loadOpenApiJson");function cc(){return(e,t)=>{try{t.status(200).type("application/json").send(fg())}catch(n){let r=n instanceof Error?n.message:String(n);t.status(500).json({error:r})}}}a(cc,"openApiJsonHandler");function mg(){return`
1177
+ })();`;ke();var Lt=a(e=>(0,Fr.createHash)("sha256").update(e).digest("hex"),"hash"),ln=a(()=>(0,Fr.randomBytes)(32).toString("base64url"),"secret"),Qm=36e5,eg=25,tg=5,ng=500,nc=a(()=>({clients:[],codes:[],tokens:[]}),"emptyDb"),ds=class{constructor(t,n){this.maxClients=n;this.path=(0,zi.join)(t.root,"oauth.json");try{let r=(0,Wn.existsSync)(this.path)?JSON.parse((0,Wn.readFileSync)(this.path,"utf8")):nc();if(!r||typeof r!="object"||!("clients"in r)||!("codes"in r)||!("tokens"in r)||!Array.isArray(r.clients)||!Array.isArray(r.codes)||!Array.isArray(r.tokens))throw new Error("Invalid OAuth store");this.db=r}catch{this.db=nc()}(0,Wn.mkdirSync)(t.root,{recursive:!0,mode:448}),this.prune()}static{a(this,"OAuthStore")}db;path;save(){un((0,zi.join)(this.path,".."),this.path,ht(this.db,8e6,{kind:"oauth",pretty:!0}))}prune(t=Date.now()){let n=this.db.clients.length+this.db.codes.length+this.db.tokens.length,r=new Set(this.db.clients.filter(o=>o.approved?(o.lastUsedAt??(o.client_id_issued_at??0)*1e3)>t-30*864e5:(o.client_id_issued_at??0)*1e3>t-Qm).map(o=>o.client_id));this.db.clients=this.db.clients.filter(o=>r.has(o.client_id)),this.db.codes=this.db.codes.filter(o=>o.expires>t&&r.has(o.clientId)),this.db.tokens=this.db.tokens.filter(o=>r.has(o.clientId)&&o.expires>t),this.db.clients.length+this.db.codes.length+this.db.tokens.length!==n&&this.save()}getClient(t){return this.prune(),this.db.clients.find(n=>n.client_id===t)}registerClient(t){if(this.prune(),this.approvedCount()>=this.maxClients())throw new Z.InvalidClientMetadataError("Maximum active OAuth clients reached");if(this.db.clients.length-this.approvedCount()>=eg)throw new Z.InvalidClientMetadataError("Too many OAuth clients awaiting consent");if(!Array.isArray(t.redirect_uris)||!t.redirect_uris.length||t.redirect_uris.some(r=>{try{let o=new URL(r);return!!o.username||!!o.password||!!o.hash||o.protocol!=="https:"&&!(o.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(o.hostname))}catch{return!0}}))throw new Z.InvalidClientMetadataError("Redirect URIs must use HTTPS or loopback HTTP");if(t.token_endpoint_auth_method&&t.token_endpoint_auth_method!=="none")throw new Z.InvalidClientMetadataError("Only public clients are supported");let n={...t,client_secret:void 0,client_secret_expires_at:void 0,client_id:ln(),client_id_issued_at:Math.floor(Date.now()/1e3),token_endpoint_auth_method:"none",lastUsedAt:Date.now()};return this.db.clients.push(n),this.save(),n}approvedCount(){return this.db.clients.filter(t=>t.approved).length}approve(t){let n=this.db.clients.find(r=>r.client_id===t);if(!(!n||n.approved)){if(this.approvedCount()>=this.maxClients())throw new Z.InvalidClientMetadataError("Maximum active OAuth clients reached");n.approved=!0,this.save()}}get data(){return this.db}persist(){this.touchTimer&&(clearTimeout(this.touchTimer),this.touchTimer=void 0),this.save()}touchTimer;touch(){this.touchTimer||(this.touchTimer=setTimeout(()=>{this.touchTimer=void 0,this.save()},5e3),this.touchTimer.unref?.())}revokeClient(t){let n=this.db.clients.length;return this.db.clients=this.db.clients.filter(r=>r.client_id!==t),this.db.tokens=this.db.tokens.filter(r=>r.clientId!==t),this.db.codes=this.db.codes.filter(r=>r.clientId!==t),this.save(),this.db.clients.length!==n}};function Ki(e){switch(e.source){case"http-in":return`Route ${e.path} is already used by http-in node '${e.nodeName??e.nodeId??"unnamed"}'`;case"httpStatic":return`File ${e.path} is served by httpStatic`;case"middleware":return`Path prefix is already handled by mounted middleware (${e.layer??"unknown"})`;default:return`Route ${e.path} is already registered by another handler (${e.layer??"route"})`}}a(Ki,"describeOAuthRouteCollision");var Ji=a((e,t)=>e.toLowerCase().startsWith(t.toLowerCase()),"startsWithCi");function Bi(e){return e===!1?void 0:`/${typeof e=="string"?e:"/"}`.replace(/\/+/g,"/").replace(/\/$/,"")}a(Bi,"normalizeRoot");function qi(e,t){if(e!==void 0){if(!e)return t;if(Ji(t,`${e}/`))return t.slice(e.length)}}a(qi,"relativePrefix");function rg(e){let t=new Map;for(let r of e)r.id&&t.set(r.id,r);let n=a((r,o=new Set)=>{if(!r)return!0;let s=t.get(r);return s?s.type==="tab"?!s.disabled&&!s.d:s.type!=="subflow"||o.has(r)?!1:(o.add(r),e.some(i=>i.type===`subflow:${r}`&&!i.d&&n(i.z,o))):!0},"live");return n}a(rg,"makeLiveCheck");function og(e,t){if(!e||!t.length)return[];let n=[];try{e(s=>n.push(s))}catch{return[]}let r=rg(n),o=[];for(let s of n){if(s.type!=="http in"||typeof s.url!="string"||s.d)continue;let i=s.url.startsWith("/")?s.url:`/${s.url}`;!t.some(d=>Ji(i,d))||!r(s.z)||o.push({path:i,source:"http-in",nodeId:s.id,nodeName:s.name?s.name:void 0,flowId:s.z})}return o}a(og,"findHttpInCollisions");var sg=new Set(["bound dispatch","handle"]),ig=["authorize","token","register","revoke"],ag=["authorization-server","protected-resource"];function dg(e){let t=e.length;for(;t>0&&e.charCodeAt(t-1)===47;)t--;return e.slice(0,t)}a(dg,"trimTrailingSlashes");function lg(e){let t=new Set;return e.filter(n=>{let r=`${n.source}\0${n.path.toLowerCase()}`;return t.has(r)?!1:(t.add(r),!0)})}a(lg,"dedupe");function cc(e){let t=Bi(e.httpAdminRoot),n=e.httpNode?Bi(e.httpNodeRoot):void 0,r=`${t??""}/mcp/oauth/`,o=e.metadataFallback?`${t??""}/mcp/.well-known/oauth-`:`${n??""}/.well-known/oauth-`,s=a(p=>{let f=qi(t,p),m=qi(n,p),h=m?og(e.eachNode,[m]):[],g=new Set(h.map(v=>v.path.toLowerCase())),y=m?Gi(e.httpNode,[m]).filter(v=>!(v.source==="route"&&sg.has(v.layer??"")&&g.has(v.path.toLowerCase()))):[],w=f?Gi(e.httpAdmin,[f]):[];return lg([...w,...y,...h])},"scan"),i=s(r),d=s(o),l=e.fileExists??(()=>!1),c=(e.staticDirs??[]).map(p=>typeof p=="string"?{path:p,root:e.httpStaticRoot}:p),u=a((p,f)=>{for(let m of c){let h=Bi(m.root),g=dg(m.path);for(let y of p){let w=qi(h,y);!w||!l(`${g}${w}`)||f.push({path:y,source:"httpStatic",file:`${g}${w}`})}}},"checkStatic");return u(ig.map(p=>`${r}${p}`),i),u(ag.map(p=>`${o}${p}`),d),{authorization:i,metadata:d}}a(cc,"scanOAuthRouteCollisions");function cg(e){let t=a(n=>{try{return e?.[n]}catch{return}},"read");return t("_router")?.stack??t("router")?.stack??[]}a(cg,"routerStack");function ug(e){let t=e.route?.path;return t?(Array.isArray(t)?t:[t]).map(String):[]}a(ug,"layerPaths");function pg(e,t){if(e.name==="router")return!1;let n=e.regexp,r=e.matchers??(n?[s=>n.test(s)]:[]);if(!r.length)return!1;let o=a(s=>r.some(i=>!!i(s)),"matches");return o("/unrelated-ai-access-collision-probe")?!1:t.some(s=>o(`${s}token`)||o(`${s}mcp`))}a(pg,"middlewareHits");function Gi(e,t=["/.well-known/oauth-","/mcp/oauth/"]){let n=[];for(let r of cg(e)){if(r.handle?.oauthOwned)continue;let o=ug(r);for(let s of o)t.some(i=>Ji(s,i))&&n.push({path:s,source:"route",layer:r.name??"route"});!o.length&&pg(r,t)&&n.push({path:t.join(", "),source:"middleware",layer:r.name??"route"}),r.handle?.stack&&n.push(...Gi({router:{stack:r.handle.stack}},t))}return n}a(Gi,"detectOAuthRouteCollisionDetails");var ls=class{constructor(t,n,r,o,s=()=>0){this.clientsStore=t;this.resource=n;this.accessTtl=r;this.refreshTtl=o;this.refreshMax=s}static{a(this,"LocalOAuthProvider")}skipLocalPkceValidation=!1;inflight=new Map;async authorize(t,n,r){throw new Z.InvalidGrantError("Admin consent is required")}async challengeForAuthorizationCode(t,n){let r=this.findCode(t,n);return this.clientsStore.data.codes=this.clientsStore.data.codes.filter(o=>o!==r),this.clientsStore.persist(),this.inflight.set(r.hash,r),setTimeout(()=>this.inflight.delete(r.hash),3e4).unref?.(),r.challenge}async exchangeAuthorizationCode(t,n,r,o,s){let i=this.inflight.get(Lt(n))??this.findCode(t,n);if(this.inflight.delete(i.hash),this.clientsStore.data.codes=this.clientsStore.data.codes.filter(d=>d!==i),this.clientsStore.persist(),i.clientId!==t.client_id||i.expires<=Date.now()||o!==i.redirectUri||s&&Nt(s.href)!==i.resource)throw new Z.InvalidGrantError("Invalid authorization code");return this.issue(t.client_id,i.scopes,i.resource,i.user)}async exchangeRefreshToken(t,n,r,o){let s=this.clientsStore.data.tokens.find(l=>l.hash===Lt(n)&&l.type==="refresh"&&l.clientId===t.client_id);if(!s||s.expires<=Date.now())throw new Z.InvalidGrantError("Invalid refresh token");if(s.rotated)throw this.clientsStore.data.tokens=this.clientsStore.data.tokens.filter(l=>l.family!==s.family),this.clientsStore.persist(),new Z.InvalidGrantError("Refresh token reuse detected");let i=this.refreshMax()*1e3,d=s.familyStart??Date.now();if(i>0&&d+i<=Date.now())throw new Z.InvalidGrantError("Refresh token family reached its maximum lifetime");if(o&&Nt(o.href)!==s.resource)throw new Z.InvalidGrantError("Resource mismatch");if(r?.some(l=>!s.scopes.includes(l)))throw new Z.InvalidGrantError("Invalid scope");return s.rotated=!0,this.issue(t.client_id,r??s.scopes,s.resource,s.user,s.family,d)}async verifyAccessToken(t){let n=this.clientsStore.data.tokens.find(o=>o.hash===Lt(t)&&o.type==="access"&&o.expires>Date.now());if(!n||n.resource!==Nt(this.resource()))throw new Z.InvalidTokenError("Invalid access token");let r=this.clientsStore.data.clients.find(o=>o.client_id===n.clientId);if(!r)throw new Z.InvalidTokenError("Invalid client");return r.lastUsedAt=Date.now(),this.clientsStore.touch(),{token:t,clientId:n.clientId,scopes:n.scopes,expiresAt:Math.floor(n.expires/1e3),resource:new URL(n.resource),extra:{user:n.user}}}async revokeToken(t,n){let r=this.clientsStore.data.tokens.find(o=>o.hash===Lt(n.token)&&o.clientId===t.client_id);r&&(this.clientsStore.data.tokens=this.clientsStore.data.tokens.filter(o=>o.family!==r.family)),this.clientsStore.persist()}createCode(t,n,r){let o=ln();return this.clientsStore.data.codes.push({hash:Lt(o),clientId:t.client_id,challenge:n.codeChallenge,redirectUri:n.redirectUri,scopes:n.scopes??["read"],resource:this.validateResource(n.resource),expires:Date.now()+6e4,user:r}),this.clientsStore.persist(),o}validateResource(t){let n=Nt(this.resource());if(t&&Nt(t.href)!==n)throw new Z.InvalidGrantError("Resource mismatch");return n}findCode(t,n){let r=this.clientsStore.data.codes.find(o=>o.hash===Lt(n)&&o.clientId===t.client_id);if(!r||r.expires<=Date.now())throw new Z.InvalidGrantError("Invalid or expired authorization code");return r}issue(t,n,r,o,s=ln(),i=Date.now()){let d=ln(),l=ln(),c=Date.now(),u=this.refreshMax()*1e3,p=Math.min(c+this.refreshTtl()*1e3,u>0?i+u:1/0),f=this.clientsStore.data,m=f.clients.find(h=>h.client_id===t);return m&&(m.lastUsedAt=c),f.tokens.push({hash:Lt(d),type:"access",clientId:t,family:s,scopes:n,resource:r,expires:c+this.accessTtl()*1e3,user:o,rotated:!1},{hash:Lt(l),type:"refresh",clientId:t,family:s,scopes:n,resource:r,expires:p,user:o,rotated:!1,familyStart:i}),this.clientsStore.persist(),{access_token:d,token_type:"Bearer",expires_in:this.accessTtl(),refresh_token:l,scope:n.join(" ")}}};function Nt(e){let t=new URL(e);if(!["http:","https:"].includes(t.protocol)||t.username||t.password||t.search||t.hash)throw new Error("Invalid resource URL");return`${t.protocol}//${t.host}${t.pathname.replace(/\/+$/,"")}`}a(Nt,"canonicalResource");var zn=["read","propose"];function fg(e){let t=(e??"/").split("/").filter(Boolean).join("/");return t?`/${t}/`:"/"}a(fg,"consentAdminRoot");var hg=new Set(["localhost","127.0.0.1","[::1]"]);function dn(e){return typeof e=="string"?e:""}a(dn,"value");function uc(e,t,n,r){let o=a(T=>`${n().replace(/\/$/,"")}${T}`,"abs"),s=a((T,x,C,E)=>{let A=x.url;x.url="/",T(x,C,S=>{x.url=A,E(S)})},"atRoot"),i=new Map,d=a(()=>{let T=Date.now();for(let[x,C]of i)C.expires<=T&&i.delete(x)},"sweep"),l=a(T=>{d();let x=[...i].filter(([,C])=>C.clientId===T);for(;x.length>=tg;)i.delete(x.shift()[0]);return i.size<ng},"reservePending"),c=a((T,x)=>{T.setHeader("Cache-Control","no-store"),T.setHeader("X-Frame-Options","DENY"),T.setHeader("Referrer-Policy","no-referrer"),T.setHeader("X-Content-Type-Options","nosniff"),T.setHeader("Content-Security-Policy",`default-src 'none'; ${x?`script-src 'nonce-${x}'; `:""}style-src 'unsafe-inline'; connect-src 'self'; form-action 'none'; base-uri 'none'; frame-ancestors 'none'`)},"securityHeaders"),u=Qr(e.settings.httpAdminRoot),p=a(T=>{if(!re(e.settings))return[...zn];let x=gt(T);return x==="*"?[...zn]:x==="read"?["read"]:[]},"grantableFor"),f=a(T=>{let x=T.get("sec-fetch-site");if(x&&x!=="same-origin")return!1;let C=T.get("origin");if(!C)return!0;let E=new Set;try{E.add(new URL(n()).origin)}catch{}let A=T.get("host");return A&&(E.add(`http://${A}`),E.add(`https://${A}`)),E.has(C)},"sameOrigin"),m=a((T,x)=>{d();let C=T.query,E=t.clientsStore.getClient(dn(C.client_id));if(!E){x.status(400).send("Unknown OAuth client");return}let A=dn(C.redirect_uri)||(E.redirect_uris.length===1?E.redirect_uris[0]:"");if(!E.redirect_uris.some(le=>(0,rc.redirectUriMatches)(A,le))){x.status(400).send("Unregistered redirect URI");return}if(C.response_type!=="code"||C.code_challenge_method!=="S256"||!/^[A-Za-z0-9_-]{43,128}$/.test(dn(C.code_challenge))){x.status(400).send("Authorization code with PKCE S256 is required");return}let S=[...new Set((dn(C.scope)||"read").split(" ").filter(Boolean))];if(S.some(le=>!zn.includes(le))){x.status(400).send("Unsupported scope");return}let N=dn(C.resource)||r(),b;try{b=Nt(N)}catch{x.status(400).send("Invalid resource");return}if(b!==Nt(r())){x.status(400).send("Resource mismatch");return}if(!l(E.client_id)){x.status(429).send("Too many pending authorization requests; try again later");return}let O=ln();i.set(O,{clientId:E.client_id,redirect:A,challenge:dn(C.code_challenge),scopes:S,resource:N,state:dn(C.state),csrf:ln(),expires:Date.now()+10*6e4});let M=new URL(A).host,F=(0,Fr.randomBytes)(16).toString("base64");c(x,F),x.status(200).type("html").send(ec({requestId:O,tokenKey:u,adminRoot:fg(e.settings.httpAdminRoot),nonce:F,redirectHost:M,loopbackRedirect:hg.has(new URL(A).hostname),clientName:E.client_name||"Unnamed client",scopes:S,script:tc}))},"authorizePage"),h=a((T,x,C)=>{c(x),lc.default.json({limit:"4kb"})(T,x,()=>Se(e,"api")(T,x,()=>{try{d();let E=/^\/mcp\/oauth\/consent\/([A-Za-z0-9_-]+)\/?$/.exec(T.path)?.[1]??"",A=i.get(E),S=A&&t.clientsStore.getClient(A.clientId);if(!A||!S){x.status(404).json({error:"Consent request expired or unknown"});return}let N=re(e.settings)&&T.user?.username||"owner",b=A.scopes.filter(F=>p(T).includes(F));if(!b.length){x.status(403).json({error:"Your Node-RED permissions do not allow granting the requested scopes",grantable:b});return}if(T.method==="GET"){A.approver=N,x.json({client_name:S.client_name??null,redirect_host:new URL(A.redirect).host,requested:A.scopes,grantable:b,user:N,csrf:A.csrf});return}if(T.method!=="POST"){x.status(405).end();return}let O=T.body??{};if(!f(T)||O.csrf!==A.csrf||A.approver!==N){x.status(403).json({error:"Consent request verification failed"});return}i.delete(E);let M=new URL(A.redirect);if(O.decision!=="allow")M.searchParams.set("error","access_denied");else{try{t.clientsStore.approve(S.client_id)}catch{x.status(409).json({error:"Maximum active OAuth clients reached"});return}let F=t.createCode(S,{redirectUri:A.redirect,codeChallenge:A.challenge,scopes:b,resource:new URL(A.resource)},N);M.searchParams.set("code",F)}A.state&&M.searchParams.set("state",A.state),x.json({redirect:M.href})}catch(E){C(E)}}))},"consentApi"),g=(0,oc.clientRegistrationHandler)({clientsStore:t.clientsStore,rateLimit:{windowMs:36e5,max:10,standardHeaders:!0,legacyHeaders:!1}}),y=(0,ic.tokenHandler)({provider:t}),w=(0,sc.revocationHandler)({provider:t}),v,L="";return{server:a((T,x,C)=>{let E=T.path,A=E.replace(/\/+$/,"").toLowerCase();if(A==="/mcp/oauth/authorize"){T.method==="GET"?m(T,x,C):x.status(405).set("Allow","GET").end();return}if(A.startsWith("/mcp/oauth/consent/")){h(T,x,C);return}if(!["/mcp/oauth/token","/mcp/oauth/register","/mcp/oauth/revoke"].includes(A)){C();return}let S=n();if(S.startsWith("https://")||/^http:\/\/(localhost|127\.0\.0\.1)(:|\/|$)/.test(S)){if(L!==S)try{v=(0,dc.mcpAuthRouter)({provider:t,issuerUrl:new URL(S),baseUrl:new URL(`${S.replace(/\/$/,"")}/mcp/oauth/`),resourceServerUrl:new URL(r()),scopesSupported:zn,clientRegistrationOptions:{rateLimit:{windowMs:36e5,max:10}}}),L=S}catch{v=void 0}if(v){let N=T.url;T.url=T.url.slice(10)||"/",v(T,x,b=>{T.url=N,C(b)});return}}if(E==="/mcp/oauth/token"){s(y,T,x,C);return}if(E==="/mcp/oauth/register"){s(g,T,x,C);return}if(E==="/mcp/oauth/revoke"){s(w,T,x,C);return}C()},"server"),metadata:a((T,x,C)=>{let E=T.path;if(E==="/.well-known/oauth-authorization-server"){s((0,Wi.metadataHandler)({issuer:n(),authorization_endpoint:o("/mcp/oauth/authorize"),token_endpoint:o("/mcp/oauth/token"),registration_endpoint:o("/mcp/oauth/register"),revocation_endpoint:o("/mcp/oauth/revoke"),response_types_supported:["code"],code_challenge_methods_supported:["S256"],grant_types_supported:["authorization_code","refresh_token"],token_endpoint_auth_methods_supported:["none"],scopes_supported:zn}),T,x,C);return}if(E==="/.well-known/oauth-protected-resource/mcp"||E==="/.well-known/oauth-protected-resource"){s((0,Wi.metadataHandler)({resource:Nt(r()),authorization_servers:[n()],scopes_supported:zn}),T,x,C);return}C()},"metadata"),auth:a((T,x,C)=>{try{let E=x.set.bind(x);x.set=((A,S)=>(A==="WWW-Authenticate"&&typeof S=="string"&&(S=S.replace('scope="read"','scope="read propose"')),typeof A=="string"?E(A,S??""):E(A))),(0,ac.requireBearerAuth)({verifier:t,requiredScopes:["read"],resourceMetadataUrl:o(e.settings.httpNodeRoot===!1||!e.httpNode?"/mcp/.well-known/oauth-protected-resource/mcp":"/.well-known/oauth-protected-resource/mcp"),expectedResource:new URL(r())})(T,x,A=>{x.set=E,C(A)})}catch(E){C(E)}},"auth")}}a(uc,"makeOAuthHandlers");var Or=require("fs"),Gn=require("path"),pc=require("url"),fc=require("yaml"),wg={};function mg(){return typeof __dirname<"u"?__dirname:(0,Gn.dirname)((0,pc.fileURLToPath)(wg.url))}a(mg,"moduleDir");function hc(){let e=mg(),t=[(0,Gn.join)(e,"..","docs","api","openapi.yaml"),(0,Gn.join)(e,"..","..","docs","api","openapi.yaml"),(0,Gn.join)(process.cwd(),"docs","api","openapi.yaml")];for(let n of t)if((0,Or.existsSync)(n))return n;return null}a(hc,"resolveOpenApiYamlPath");function gg(){let e=hc();if(!e)throw new Error("OpenAPI specification file was not found");return(0,Or.readFileSync)(e,"utf8")}a(gg,"loadOpenApiYaml");function mc(){return(e,t)=>{try{let n=gg();t.status(200).type("application/yaml").send(n)}catch(n){let r=n instanceof Error?n.message:String(n);t.status(500).json({error:r})}}}a(mc,"openApiYamlHandler");var Vi;function yg(){let e=hc();if(!e)throw new Error("OpenAPI specification file was not found");return Vi?.path!==e&&(Vi={path:e,json:JSON.stringify((0,fc.parse)((0,Or.readFileSync)(e,"utf8")))}),Vi.json}a(yg,"loadOpenApiJson");function gc(){return(e,t)=>{try{t.status(200).type("application/json").send(yg())}catch(n){let r=n instanceof Error?n.message:String(n);t.status(500).json({error:r})}}}a(gc,"openApiJsonHandler");function bg(){return`
1178
1178
  var settingsData = null;
1179
1179
  var settingsSaving = false;
1180
1180
  var settingsTimer = null;
@@ -1285,9 +1285,9 @@ ${os}
1285
1285
  var payload = {
1286
1286
  enabled: $('set-enabled').checked,
1287
1287
  allowWithoutAdminAuth: $('set-allow-without').checked,
1288
- defaultLinkTtlSeconds: Math.max(1, Math.floor(Number($('set-ttl').value || 4) * 3600)),
1288
+ defaultLinkTtlSeconds: Number($('set-ttl').value) * 3600,
1289
1289
  defaultDeploymentType: $('set-deploy').value,
1290
- proposalTtlDays: Math.min(365, Math.max(1, Math.floor(Number($('set-proposal-ttl').value) || 7))),
1290
+ proposalTtlDays: Number($('set-proposal-ttl').value),
1291
1291
  maxActiveLinks: Number($('set-max-links').value),
1292
1292
  publicBaseUrl: String($('set-public-url').value).trim(),
1293
1293
  corsAllowedOrigins: String($('set-cors').value).split(',').map(function (x) { return x.trim(); }).filter(Boolean),
@@ -1312,7 +1312,7 @@ ${os}
1312
1312
  return payload;
1313
1313
  }
1314
1314
 
1315
- `}a(mg,"settingsPartForm");function gg(){return`
1315
+ `}a(bg,"settingsPartForm");function vg(){return`
1316
1316
  function secretPatternsEditable() {
1317
1317
  return settingsCanEdit() && !fieldLocked('secretNamePatterns');
1318
1318
  }
@@ -1400,7 +1400,7 @@ ${os}
1400
1400
  });
1401
1401
  }
1402
1402
 
1403
- `}a(gg,"settingsPartSecrets");function yg(){return`
1403
+ `}a(vg,"settingsPartSecrets");function kg(){return`
1404
1404
  function settingsFlash(msg, ok) {
1405
1405
  var tab = document.querySelector('nav.tabs button[aria-selected="true"]');
1406
1406
  var target = tab && tab.getAttribute('data-tab') === 'oauth' ? 'oauth-flash' : 'settings-flash';
@@ -1423,7 +1423,6 @@ ${os}
1423
1423
  loadOAuthStatus();
1424
1424
  } catch (e) {
1425
1425
  settingsFlash(e.message || String(e), false);
1426
- loadSettings();
1427
1426
  } finally {
1428
1427
  settingsSaving = false;
1429
1428
  }
@@ -1431,6 +1430,20 @@ ${os}
1431
1430
 
1432
1431
  function saveSettingsNow() {
1433
1432
  if (!settingsCanEdit() || settingsSaving) return;
1433
+ var fields = ['set-ttl', 'set-proposal-ttl', 'set-max-links', 'set-rate-window', 'set-rate-ip', 'set-rate-token',
1434
+ 'oauth-access-ttl', 'oauth-refresh-ttl', 'oauth-refresh-max', 'oauth-dcr-cap'];
1435
+ for (var i = 0; i < fields.length; i++) {
1436
+ var input = $(fields[i]);
1437
+ if (!input || input.disabled) continue;
1438
+ var n = Number(input.value), min = Number(input.min || 0);
1439
+ var max = input.max ? Number(input.max) : Infinity;
1440
+ if (!input.value.trim() || !Number.isInteger(n) || n < min || n > max) {
1441
+ input.setAttribute('aria-invalid', 'true');
1442
+ settingsFlash('Enter a whole number with a minimum of ' + min + (max < Infinity ? ' and a maximum of ' + max : '') + '.', false);
1443
+ return;
1444
+ }
1445
+ input.removeAttribute('aria-invalid');
1446
+ }
1434
1447
  var payload = collectSettingsPayload();
1435
1448
  var was = settingsData && settingsData.settings && settingsData.settings.oauth && settingsData.settings.oauth.enabled;
1436
1449
  if (!was && payload.oauth && payload.oauth.enabled) {
@@ -1473,6 +1486,7 @@ ${os}
1473
1486
  el.className = 'check-result' + (state ? ' is-' + state : '');
1474
1487
  el.textContent = text || '';
1475
1488
  el.hidden = !text;
1489
+ el.setAttribute('role', state === 'fail' ? 'alert' : 'status');
1476
1490
  }
1477
1491
 
1478
1492
  async function checkPublicUrl() {
@@ -1488,7 +1502,7 @@ ${os}
1488
1502
  }
1489
1503
  }
1490
1504
 
1491
- `}a(yg,"settingsPartSave");function wg(){return`
1505
+ `}a(kg,"settingsPartSave");function Ag(){return`
1492
1506
  function editorNodeHref(c) {
1493
1507
  var base = editorRoot && editorRoot !== '/' ? editorRoot.replace(/\\/$/, '') + '/' : '/';
1494
1508
  if (c.nodeId) return base + '#node/' + encodeURIComponent(c.nodeId);
@@ -1596,7 +1610,7 @@ ${os}
1596
1610
  }
1597
1611
  }
1598
1612
 
1599
- `}a(wg,"settingsPartOAuth");function bg(){return`
1613
+ `}a(Ag,"settingsPartOAuth");function xg(){return`
1600
1614
  function loadOAuthTab() {
1601
1615
  loadSettings();
1602
1616
  loadOAuthStatus();
@@ -1629,7 +1643,7 @@ ${os}
1629
1643
  showToast('Server URL copied.', true);
1630
1644
  });
1631
1645
  })();
1632
- `}a(bg,"settingsPartWire");function uc(){return mg()+gg()+yg()+wg()+bg()}a(uc,"pageClientSettingsScript");function kg(){return`
1646
+ `}a(xg,"settingsPartWire");function yc(){return bg()+vg()+kg()+Ag()+xg()}a(yc,"pageClientSettingsScript");function Tg(){return`
1633
1647
  (function () {
1634
1648
  var root = document.getElementById('mcp-root');
1635
1649
  if (!root) return;
@@ -1713,6 +1727,7 @@ ${os}
1713
1727
  function showFlash(el, message, ok) {
1714
1728
  if (!el) return;
1715
1729
  el.className = 'flash ' + (ok ? 'ok' : 'error');
1730
+ el.setAttribute('role', ok ? 'status' : 'alert');
1716
1731
  el.textContent = message;
1717
1732
  el.hidden = !message;
1718
1733
  }
@@ -1765,6 +1780,7 @@ ${os}
1765
1780
  if (!st) return;
1766
1781
  st.textContent = text;
1767
1782
  st.className = 'sg-status' + (isError ? ' error' : '');
1783
+ st.setAttribute('role', isError ? 'alert' : 'status');
1768
1784
  st.hidden = !text;
1769
1785
  }
1770
1786
  function authGateContinue() {
@@ -1860,7 +1876,7 @@ ${os}
1860
1876
  }
1861
1877
  });
1862
1878
  }
1863
- ${os}
1879
+ ${as}
1864
1880
  function isStatusPath(path) {
1865
1881
  return path === '/status' || path.indexOf('/status?') === 0;
1866
1882
  }
@@ -1875,6 +1891,7 @@ ${os}
1875
1891
  opts = opts || {};
1876
1892
  var el = document.createElement('div');
1877
1893
  el.className = 'toast ' + (ok ? 'ok' : 'error');
1894
+ el.setAttribute('role', ok ? 'status' : 'alert');
1878
1895
  if (opts.title) {
1879
1896
  var titleEl = document.createElement('span');
1880
1897
  titleEl.className = 'toast-title';
@@ -1948,7 +1965,11 @@ ${os}
1948
1965
  topic: 'Topic',
1949
1966
  payload: 'Payload',
1950
1967
  complete: 'Complete',
1951
- info: 'Info',
1968
+ info: 'Description',
1969
+ label: 'Flow name',
1970
+ env: 'Environment variables',
1971
+ disabled: 'Flow disabled',
1972
+ credentials: 'Credentials',
1952
1973
  format: 'Format',
1953
1974
  syntax: 'Syntax',
1954
1975
  outputs: 'Outputs',
@@ -1993,7 +2014,7 @@ ${os}
1993
2014
  }
1994
2015
  return body;
1995
2016
  }
1996
- `}a(kg,"pageClientScriptPartA");function Ag(){return`
2017
+ `}a(Tg,"pageClientScriptPartA");function Rg(){return`
1997
2018
  // Time helpers \u2014 keep in sync with page-time.ts (always browser-local Intl).
1998
2019
  function titleFromDisplayName(displayName) {
1999
2020
  var s = String(displayName || '');
@@ -2100,7 +2121,7 @@ ${os}
2100
2121
  btn.disabled = false;
2101
2122
  }, 1600);
2102
2123
  }
2103
- `}a(Ag,"pageClientScriptPartATimeCopy");function Sg(){return`
2124
+ `}a(Rg,"pageClientScriptPartATimeCopy");function Pg(){return`
2104
2125
  function shortId(value, keep) {
2105
2126
  var s = String(value == null ? '' : value);
2106
2127
  var n = keep || 8;
@@ -2236,7 +2257,7 @@ ${os}
2236
2257
  }
2237
2258
  return '';
2238
2259
  }
2239
- `}a(Sg,"pageClientScriptPartAFormat");function xg(){return`
2260
+ `}a(Pg,"pageClientScriptPartAFormat");function _g(){return`
2240
2261
  function lookupCachedProposal(id) {
2241
2262
  if (!id) return null;
2242
2263
  return cachedProposals.filter(function (x) { return x.id === id; })[0] || null;
@@ -2357,7 +2378,7 @@ ${os}
2357
2378
  return k + '=' + String(v);
2358
2379
  }).join(', '));
2359
2380
  }
2360
- `}a(xg,"pageClientScriptPartAAudit");function Tg(){return`
2381
+ `}a(_g,"pageClientScriptPartAAudit");function Cg(){return`
2361
2382
  // Hash helpers \u2014 keep in sync with page-hash.ts
2362
2383
  var TAB_IDS = ['status', 'settings', 'links', 'oauth', 'proposals'];
2363
2384
  function hashForTab(tab) {
@@ -2643,7 +2664,7 @@ ${os}
2643
2664
  }
2644
2665
  }
2645
2666
 
2646
- `}a(Tg,"pageClientScriptPartATabs");function Rg(){return`
2667
+ `}a(Cg,"pageClientScriptPartATabs");function Eg(){return`
2647
2668
  async function loadLinks(opts) {
2648
2669
  opts = opts || {};
2649
2670
  if (!aiOn || !canManage) {
@@ -2717,7 +2738,7 @@ ${os}
2717
2738
  showToast(e.message || String(e), false);
2718
2739
  }
2719
2740
  }
2720
- `}a(Rg,"pageClientScriptPartA2");function Pg(){return`
2741
+ `}a(Eg,"pageClientScriptPartA2");function Ig(){return`
2721
2742
 
2722
2743
  function normalizeLinkKind(kind) {
2723
2744
  if (kind === 'rest' || kind === 'mcp' || kind === 'get_link') return kind;
@@ -3175,6 +3196,7 @@ ${os}
3175
3196
 
3176
3197
  function plainDiffSentence(diff, flowLabel) {
3177
3198
  if (!diff || !diff.summary) return '';
3199
+ if (proposalHasNoNetChange(diff)) return 'Nothing changes in the live flow';
3178
3200
  var sum = diff.summary;
3179
3201
  var parts = [];
3180
3202
  if (sum.added) parts.push('Adds ' + sum.added + (sum.added === 1 ? ' node' : ' nodes'));
@@ -3182,7 +3204,8 @@ ${os}
3182
3204
  (sum.removed === 1 ? ' node' : ' nodes'));
3183
3205
  if (sum.modified) parts.push((parts.length ? 'changes ' : 'Changes ') + sum.modified +
3184
3206
  (sum.modified === 1 ? ' node' : ' nodes'));
3185
- if (!parts.length) parts.push('No node changes');
3207
+ if ((sum.tabChanges || []).length) parts.push('Changes flow settings');
3208
+ if (!parts.length) parts.push('No changes');
3186
3209
  var sentence = parts.join(', ');
3187
3210
  if (flowLabel) sentence += ' in ' + flowLabel;
3188
3211
  return sentence;
@@ -3216,10 +3239,14 @@ ${os}
3216
3239
 
3217
3240
  /** Soft validation notes from the proposal and/or its diff. */
3218
3241
  function proposalWarnings(proposal, diff) {
3219
- var fromDiff = diff && Array.isArray(diff.warnings) ? diff.warnings : [];
3220
- if (fromDiff.length) return fromDiff;
3221
- if (proposal && Array.isArray(proposal.warnings)) return proposal.warnings;
3222
- return [];
3242
+ var all = (proposal && Array.isArray(proposal.warnings) ? proposal.warnings : [])
3243
+ .concat(diff && Array.isArray(diff.warnings) ? diff.warnings : []);
3244
+ var seen = {};
3245
+ return all.filter(function (w) {
3246
+ var key = JSON.stringify([w.code, w.nodeId, w.property, w.error]);
3247
+ if (seen[key]) return false;
3248
+ seen[key] = true; return true;
3249
+ });
3223
3250
  }
3224
3251
 
3225
3252
  /**
@@ -3228,33 +3255,22 @@ ${os}
3228
3255
  */
3229
3256
  var SECRET_WARN_CODES = ['secret_destination_changed', 'secret_node_code_changed',
3230
3257
  'secret_wires_changed', 'secret_scope_changed', 'outbound_node_changed', 'secret_tab_changed',
3231
- 'secret_reference_added'];
3232
- function renderRestoredSecrets(p) {
3258
+ 'secret_reference_added', 'secret_may_revert'];
3259
+ function renderReviewWarnings(p, diff) {
3260
+ if (proposalHasNoNetChange(diff)) return '';
3261
+ var warnings = proposalWarnings(p, diff);
3262
+ var security = warnings.filter(function (w) { return SECRET_WARN_CODES.indexOf(w.code) >= 0; });
3263
+ var config = warnings.filter(function (w) { return SECRET_WARN_CODES.indexOf(w.code) < 0; });
3233
3264
  var fields = p && Array.isArray(p.restoredSecrets) ? p.restoredSecrets : [];
3234
- var warns = (p && Array.isArray(p.warnings) ? p.warnings : []).filter(function (w) {
3235
- return w && SECRET_WARN_CODES.indexOf(w.code) >= 0;
3236
- });
3237
- if (!fields.length && !warns.length) return '';
3238
- var strong = warns.some(function (w) { return w.code !== 'secret_node_code_changed'; });
3239
- var items = fields.map(function (f) {
3240
- return '<li><code>' + esc(String(f.nodeId)) + '</code> \xB7 <code>' + esc(String(f.property)) + '</code></li>';
3241
- }).join('');
3242
- var warnItems = warns.map(function (w) {
3243
- return '<li><strong>' + esc(String(w.code)) + '</strong> \u2014 ' + esc(String(w.error || '')) + '</li>';
3244
- }).join('');
3245
- return '<div class="proposal-warnings restored-secrets' + (strong ? ' restored-secrets-danger' : '') +
3246
- '" role="' + (strong ? 'alert' : 'status') + '">' +
3247
- '<p class="proposal-warnings-title">' +
3248
- esc(strong
3249
- ? 'Security review: this proposal changes what could send data (or secrets) elsewhere'
3250
- : 'Secrets restored from the live flow (' + fields.length + ')') +
3251
- '</p>' +
3252
- (warnItems ? '<ul>' + warnItems + '</ul>' : '') +
3253
- (fields.length
3254
- ? '<p class="muted">The AI saw [REDACTED] in these fields; approving keeps the real values:</p>' +
3255
- '<ul>' + items + '</ul>'
3256
- : '') +
3257
- '</div>';
3265
+ var count = warnings.length + fields.length;
3266
+ if (!count) return '';
3267
+ return '<aside class="proposal-warnings review-warnings" role="' + (security.length ? 'alert' : 'status') + '">' +
3268
+ '<h3>Review notes (' + count + ')</h3>' +
3269
+ renderValidationWarnings(security, { title: 'Security review (' + security.length + ')' }) +
3270
+ (fields.length ? '<details class="restored-secrets"><summary>Secrets retained from the live flow (' + fields.length + ')</summary><ul>' + fields.map(function (f) {
3271
+ return '<li><code>' + esc(f.nodeId) + '</code> \xB7 ' + esc(f.property) + '</li>';
3272
+ }).join('') + '</ul></details>' : '') +
3273
+ renderValidationWarnings(config, { title: 'Needs configuration (' + config.length + ')' }) + '</aside>';
3258
3274
  }
3259
3275
 
3260
3276
  function renderValidationWarnings(warnings, opts) {
@@ -3447,7 +3463,7 @@ ${os}
3447
3463
  decided: Math.max(decidedTotal, cachedProposals.filter(function (p) { return (p.status !== 'pending' && p.status !== 'conflict'); }).length)
3448
3464
  };
3449
3465
  }
3450
- `}a(Pg,"pageClientScriptPartB");function _g(){return`
3466
+ `}a(Ig,"pageClientScriptPartB");function Lg(){return`
3451
3467
  var DIFF_MODE_KEY = 'ai-access-diff-mode';
3452
3468
  var DIFF_SPLIT_MIN_PX = ${900};
3453
3469
  var PRESENCE_SHORT_TEXT_MAX = ${120};
@@ -3564,7 +3580,7 @@ ${os}
3564
3580
  '</div>';
3565
3581
  }
3566
3582
 
3567
- `}a(_g,"pageClientScriptPartBDiff");function Eg(){return`
3583
+ `}a(Lg,"pageClientScriptPartBDiff");function Ng(){return`
3568
3584
  function isCollapsiblePresenceText(value) {
3569
3585
  var s = String(value == null ? '' : value);
3570
3586
  if (s.indexOf('\\n') >= 0) return true;
@@ -3667,7 +3683,7 @@ ${os}
3667
3683
  });
3668
3684
  });
3669
3685
  }
3670
- `}a(Eg,"pageClientScriptPartBDiffPresence");function Cg(){return`
3686
+ `}a(Ng,"pageClientScriptPartBDiffPresence");function Fg(){return`
3671
3687
  function textChangeLineCount(ch) {
3672
3688
  return (ch.lines || []).length;
3673
3689
  }
@@ -3726,10 +3742,13 @@ ${os}
3726
3742
  '<code class="diff-before" title="before">' + esc(JSON.stringify(ch.before)) + '</code>' +
3727
3743
  ' \u2192 <code class="diff-after" title="after">' + esc(JSON.stringify(ch.after)) + '</code></div>';
3728
3744
  }
3729
- `}a(Cg,"pageClientScriptPartBDiffView");function Ig(){return`
3745
+ `}a(Fg,"pageClientScriptPartBDiffView");function Og(){return`
3730
3746
  function renderDiffSections(sections, mode, emptyMsg) {
3731
3747
  var parts = sections.summary && (sections.summary.tabChanges || []).length
3732
3748
  ? '<p>Flow settings changed: ' + esc(sections.summary.tabChanges.join(', ')) + '</p>' : '';
3749
+ if (sections.tabProperties && sections.tabProperties.length) {
3750
+ parts += sections.tabProperties.map(function (ch) { return renderPropertyChange(ch, mode); }).join('');
3751
+ }
3733
3752
  if (sections.added && sections.added.length) {
3734
3753
  parts += '<div class="diff-section"><h4>Added nodes</h4>' +
3735
3754
  sections.added.map(function (n) { return renderPresenceNode(n, 'added'); }).join('') +
@@ -3763,6 +3782,23 @@ ${os}
3763
3782
  return parts;
3764
3783
  }
3765
3784
 
3785
+ function diffHasChanges(diff) {
3786
+ if (!diff || !diff.summary) return false;
3787
+ var sum = diff.summary;
3788
+ return !!(sum.added || sum.removed || sum.modified || (sum.tabChanges || []).length);
3789
+ }
3790
+ function proposalHasNoNetChange(diff) {
3791
+ return !!(diff && diff.rebase && diff.rebase.state !== 'conflict' &&
3792
+ diff.vsCurrent && diff.vsCurrent.summary && !diffHasChanges(diff.vsCurrent));
3793
+ }
3794
+ function diffComparisonKey(diff) {
3795
+ if (!diff) return '';
3796
+ return JSON.stringify([diff.summary && diff.summary.tabChanges, diff.tabProperties || [],
3797
+ (diff.added || []).map(function (n) { return [n.id, n.properties]; }),
3798
+ (diff.removed || []).map(function (n) { return [n.id, n.properties]; }),
3799
+ (diff.modified || []).map(function (n) { return [n.id, n.changes]; })]);
3800
+ }
3801
+
3766
3802
  function renderHumanDiff(diff, mode) {
3767
3803
  if (!diff) return '<p class="muted">Diff unavailable.</p>';
3768
3804
  mode = mode || getDiffMode();
@@ -3784,61 +3820,121 @@ ${os}
3784
3820
  (mode === 'unified' ? 'true' : 'false') + '">Unified</button>' +
3785
3821
  '</div></div>';
3786
3822
  }
3787
- var warnHtml = renderValidationWarnings(diff.warnings, {
3788
- title: 'Scaffold warnings \u2014 configure before deploy if needed',
3789
- className: 'diff-warnings'
3790
- });
3791
- var sections = renderDiffSections(
3792
- diff,
3793
- mode,
3794
- diff.compareAgainst === 'base'
3795
- ? 'No node changes compared with what the AI started from.'
3796
- : 'No node changes compared with the live flow.'
3797
- );
3798
- var liveChanges = diff.liveChanges;
3799
- var liveHtml = liveChanges && ((liveChanges.added || []).length ||
3800
- (liveChanges.removed || []).length || (liveChanges.modified || []).length ||
3801
- (liveChanges.summary && (liveChanges.summary.tabChanges || []).length))
3802
- ? '<h3>Changed in editor since submit</h3>' + renderDiffSections(liveChanges, mode, 'No editor node changes.') : '';
3823
+ var sections = renderDiffSections(diff, mode, 'Nothing changes.');
3824
+ var liveChanged = diffHasChanges(diff.liveChanges);
3825
+ var identical = diff.vsCurrent && diffComparisonKey(diff) === diffComparisonKey(diff.vsCurrent);
3826
+ var liveHtml = liveChanged
3827
+ ? '<details class="editor-changes"><summary>Changed in editor since submit</summary>' +
3828
+ renderDiffSections(diff.liveChanges, mode, 'No editor changes.') + '</details>' : '';
3803
3829
  var deployHtml = diff.vsCurrent
3804
- ? renderDiffSections(diff.vsCurrent, mode, 'No node changes compared with the live flow.')
3805
- : '<p class="muted">' + (diff.rebase && diff.rebase.state === 'conflict'
3806
- ? 'Resolve conflicts to preview what will deploy.' : 'Deployment preview unavailable.') + '</p>';
3807
- return missing + warnHtml + toolbar + '<h3>Proposed change (vs base)</h3>' + sections +
3830
+ ? renderDiffSections(diff.vsCurrent, mode, 'Nothing changes in the live flow.')
3831
+ : '<p class="muted">Resolve conflicts to preview what will deploy.</p>';
3832
+ if (proposalHasNoNetChange(diff)) {
3833
+ return '<p class="no-net-change" role="status"><strong>Nothing changes in the live flow.</strong> ' +
3834
+ 'The proposal is already reflected in the editor. You can reject it to close it.</p>' + liveHtml;
3835
+ }
3836
+ if (identical || (!liveChanged && !(diff.rebase && diff.rebase.state === 'conflict'))) {
3837
+ return missing + toolbar + '<h3>Proposed change</h3>' + sections + liveHtml;
3838
+ }
3839
+ return missing + toolbar + '<h3>Proposed change (vs base)</h3>' + sections +
3808
3840
  liveHtml + '<h3>Will deploy (vs live)</h3>' + deployHtml;
3809
3841
  }
3810
3842
 
3843
+ function conflictReason(c) {
3844
+ var reasons = {
3845
+ node_changed_both: 'The editor and proposal changed the same fields on this node.',
3846
+ tab_changed_both: 'The editor and proposal changed the same flow settings.',
3847
+ node_deleted_live: 'This node was deleted in the editor and edited in the proposal.',
3848
+ node_deleted_in_proposal_modified_live: 'The proposal deletes a node that was edited in the editor.',
3849
+ id_collision: 'The editor and proposal added different nodes with the same ID.',
3850
+ tab_deleted: 'This flow was deleted in the editor.',
3851
+ base_unavailable: 'The original flow snapshot is unavailable. Ask the AI to submit a new proposal.',
3852
+ dangling_reference: 'A connection or configuration refers to a missing node.',
3853
+ redacted_placeholder: 'A hidden secret could not be matched safely. Review it in the editor.'
3854
+ };
3855
+ return reasons[c.kind] || c.message || 'Review the competing changes.';
3856
+ }
3857
+ function conflictValueText(value) {
3858
+ return value === undefined ? '(not present)' : typeof value === 'string' ? value : JSON.stringify(value, null, 2);
3859
+ }
3860
+ function renderConflictValues(c, diff) {
3861
+ var entry = ((diff && diff.conflictValues) || []).filter(function (v) { return v.nodeId === (c.nodeId || ''); })[0];
3862
+ if (!entry) return '<p class="muted">Loading before and after values\u2026</p>';
3863
+ return entry.fields.map(function (v) {
3864
+ return '<div class="conflict-values"><h4>' + esc(v.field || 'Node') + '</h4><dl>' +
3865
+ ['base', 'live', 'proposed'].map(function (side) {
3866
+ return '<div><dt>' + (side === 'base' ? 'Base (before submission)' : side === 'live' ? 'Live (in editor)' : 'Proposed (by AI)') +
3867
+ '</dt><dd><pre>' + esc(conflictValueText(v[side])) + '</pre></dd></div>';
3868
+ }).join('') + '</dl></div>';
3869
+ }).join('');
3870
+ }
3871
+
3811
3872
  function renderConflictPanel(p, diff) {
3812
3873
  var rebase = (diff && diff.rebase) || p.rebase;
3813
- if (!rebase || rebase.state !== 'conflict') return '';
3814
- return '<div class="proposal-conflicts" role="region" aria-label="Proposal conflicts">' +
3874
+ var selected = p.conflictChoice;
3875
+ var chosen = selected ? '<div class="conflict-choice" role="status"><strong>Chosen: ' +
3876
+ (selected.choice === 'live' ? 'Keep live' : 'Use proposed') + '</strong> \xB7 ' + esc(selected.nodeId) +
3877
+ (canApprove ? ' <button type="button" class="action secondary resolve-conflict-btn" data-id="' + esc(p.id) +
3878
+ '" data-node-id="' + esc(selected.nodeId) + '" data-choice="undo">Undo / change choice</button>' : '') +
3879
+ '<p>This choice changes the proposal only. Nothing is deployed until approval. Undo is available until the next proposal edit or choice.</p></div>' : '';
3880
+ if ((p.aiConflictOverrides || []).length) {
3881
+ chosen += '<div class="warning ai-conflict-override" role="alert"><strong>AI chose its own value over your editor change</strong>' +
3882
+ '<p>Review the fields below before approving this proposal.</p>' +
3883
+ ((diff && diff.aiOverrideValues) || []).map(function (entry) {
3884
+ return '<h4>' + esc(entry.nodeId) + '</h4>' + entry.fields.map(function (v) {
3885
+ return '<div class="conflict-values"><h4>' + esc(v.field || 'Node') + '</h4><dl>' +
3886
+ '<div><dt>Before (live editor)</dt><dd><pre>' + esc(conflictValueText(v.live)) + '</pre></dd></div>' +
3887
+ '<div><dt>After (AI proposed)</dt><dd><pre>' + esc(conflictValueText(v.proposed)) + '</pre></dd></div></dl></div>';
3888
+ }).join('');
3889
+ }).join('') + (!diff ? '<p>Loading before and after values\u2026</p>' : '') + '</div>';
3890
+ }
3891
+ if (!rebase || rebase.state !== 'conflict') return chosen;
3892
+ return chosen + '<div class="proposal-conflicts" role="region" aria-label="Proposal conflicts">' +
3815
3893
  '<h3>Conflicts</h3><p>Resolve these conflicts before approving.</p>' +
3816
3894
  (rebase.conflicts || []).map(function (c) {
3817
- return '<div class="proposal-conflict"><strong>' + esc(c.nodeName || 'Unnamed node') + '</strong>' +
3818
- ' \xB7 ' + esc(c.nodeType || 'unknown type') + ' \xB7 <code>' + esc(c.nodeId || '') + '</code>' +
3819
- '<p>' + esc(c.kind) + '</p><p>Fields: ' + esc((c.fields || []).join(', ')) + '</p>' +
3820
- '<p>' + esc(c.message) + '</p>' + (canApprove ? (c.choices || []).filter(function (choice) {
3895
+ var tabConflict = c.nodeId === proposalFlowId(p) || /^tab_/.test(c.kind || '');
3896
+ return '<div class="proposal-conflict"><strong>' + esc(flowLabelOf(p) || 'Flow') + ' \xB7 ' +
3897
+ esc(c.nodeName || (tabConflict ? 'Flow settings' : 'Unnamed node')) + '</strong>' +
3898
+ ' \xB7 ' + esc(c.nodeType || (tabConflict ? 'tab' : 'unknown type')) + ' \xB7 <code>' + esc(c.nodeId || '') + '</code>' +
3899
+ '<p>' + esc(conflictReason(c)) + '</p>' + renderConflictValues(c, diff) +
3900
+ (canApprove ? (c.choices || []).filter(function (choice) {
3821
3901
  return choice === 'live' || choice === 'proposed';
3822
3902
  }).map(function (choice) {
3823
- return '<button type="button" class="action secondary resolve-conflict-btn" data-id="' + esc(p.id) +
3903
+ return '<div class="conflict-option"><button type="button" class="action secondary resolve-conflict-btn" data-id="' + esc(p.id) +
3824
3904
  '" data-node-id="' + esc(c.nodeId || '') + '" data-choice="' + esc(choice) + '">' +
3825
- (choice === 'live' ? 'Keep live' : 'Use proposed') + '</button> ';
3905
+ (choice === 'live' ? 'Keep live' : 'Use proposed') + '</button>' +
3906
+ '<p class="hint">' + (choice === 'live' ? 'Keep the editor value for the conflicting fields.' :
3907
+ 'Replace the conflicting editor fields with the AI values.') + ' Other field changes are preserved.</p></div>';
3826
3908
  }).join('') : '') + '</div>';
3827
3909
  }).join('') + (canApprove ? '<button type="button" class="action secondary recheck-btn" data-id="' +
3828
3910
  esc(p.id) + '">Re-check</button>' : '') + '</div>';
3829
3911
  }
3830
3912
 
3831
- async function updateProposalConflict(id, nodeId, choice) {
3913
+ async function updateProposalConflict(id, nodeId, choice, button) {
3914
+ proposalActionsInFlight += 1;
3915
+ var label = button && button.textContent;
3916
+ if (button) {
3917
+ button.disabled = true;
3918
+ button.setAttribute('aria-busy', 'true');
3919
+ button.innerHTML = '<span class="spinner" aria-hidden="true"></span> ' + (choice ? 'Saving\u2026' : 'Re-checking\u2026');
3920
+ }
3832
3921
  try {
3833
- await api('/proposals/' + encodeURIComponent(id) + (choice ? '/resolve-conflict' : '/recheck'), {
3922
+ var result = await api('/proposals/' + encodeURIComponent(id) + (choice ? '/resolve-conflict' : '/recheck'), {
3834
3923
  method: 'POST', body: JSON.stringify(choice ? { nodeId: nodeId, choice: choice } : {})
3835
3924
  });
3836
3925
  delete proposalDiffCache[id];
3837
3926
  await loadProposals();
3838
3927
  await loadProposalDiff(id);
3928
+ var current = cachedProposals.filter(function (p) { return p.id === id; })[0];
3929
+ var conflict = current ? proposalIsStale(current) : result && result.proposal && result.proposal.status === 'conflict';
3930
+ showToast(choice === 'undo' ? 'Choice undone. Review the conflict again.' :
3931
+ conflict ? 'Still conflicting. Review the remaining conflicts.' : 'Resolved. Review the updated proposal.', true);
3839
3932
  notifySidebarPendingRefresh();
3840
3933
  } catch (e) {
3841
3934
  showFlash($('proposals-flash'), e.message || String(e), false);
3935
+ } finally {
3936
+ proposalActionsInFlight -= 1;
3937
+ if (button) { button.disabled = false; button.removeAttribute('aria-busy'); button.textContent = label; }
3842
3938
  }
3843
3939
  }
3844
3940
 
@@ -3852,7 +3948,7 @@ ${os}
3852
3948
  // Full view kept for mode-toggle re-render inside .proposal-changes.
3853
3949
  return renderHumanDiff(diff, mode);
3854
3950
  }
3855
- `}a(Ig,"pageClientScriptPartBDiffView2");function Lg(){return`
3951
+ `}a(Og,"pageClientScriptPartBDiffView2");function $g(){return`
3856
3952
  function updatePendingBadges() {
3857
3953
  var counts = countPendingBuckets();
3858
3954
  var fullText = formatPendingBadgeText(counts.live, counts.stale);
@@ -3864,12 +3960,12 @@ ${os}
3864
3960
  badge.title = fullText;
3865
3961
  }
3866
3962
  var filterCount = $('filter-pending-count');
3867
- if (filterCount) filterCount.textContent = fullText;
3963
+ if (filterCount) { filterCount.textContent = String(counts.pending); filterCount.title = fullText; }
3868
3964
  var decidedCount = $('filter-decided-count');
3869
3965
  if (decidedCount) decidedCount.textContent = String(counts.decided);
3870
3966
  var dashPending = $('dash-pending');
3871
3967
  if (dashPending) {
3872
- dashPending.textContent = fullText;
3968
+ dashPending.textContent = String(counts.pending);
3873
3969
  dashPending.title = fullText;
3874
3970
  }
3875
3971
  }
@@ -3908,11 +4004,18 @@ ${os}
3908
4004
  });
3909
4005
  });
3910
4006
  list.querySelectorAll('.resolve-conflict-btn, .recheck-btn').forEach(function (btn) {
3911
- bindOnce(btn, 'click', async function () {
4007
+ bindOnce(btn, 'click', function () {
3912
4008
  if (btn.disabled) return;
3913
- btn.disabled = true;
3914
- await updateProposalConflict(btn.getAttribute('data-id'), btn.getAttribute('data-node-id'), btn.getAttribute('data-choice'));
3915
- btn.disabled = false;
4009
+ var id = btn.getAttribute('data-id'), nodeId = btn.getAttribute('data-node-id'), choice = btn.getAttribute('data-choice');
4010
+ if (!choice) { updateProposalConflict(id, nodeId, choice, btn); return; }
4011
+ openConfirmDialog({
4012
+ title: choice === 'undo' ? 'Undo this choice?' : choice === 'live' ? 'Keep live values?' : 'Use proposed values?',
4013
+ message: choice === 'undo' ? 'Restore the proposal before the latest choice, then re-check against the current editor.' :
4014
+ (choice === 'live' ? 'The conflicting fields will keep the editor values.' : 'The conflicting fields will use the AI values.') +
4015
+ ' Independent field changes are preserved. This updates the proposal; it does not deploy.',
4016
+ confirmLabel: choice === 'undo' ? 'Undo choice' : 'Confirm choice',
4017
+ onConfirm: function () { updateProposalConflict(id, nodeId, choice, btn); }
4018
+ });
3916
4019
  });
3917
4020
  });
3918
4021
  list.querySelectorAll('.approve-btn').forEach(function (btn) {
@@ -3958,7 +4061,7 @@ ${os}
3958
4061
  });
3959
4062
  });
3960
4063
  }
3961
- `}a(Lg,"pageClientScriptPartB2");function Ng(){return`
4064
+ `}a($g,"pageClientScriptPartB2");function Mg(){return`
3962
4065
  function renderDeleteBanner(diff) {
3963
4066
  if (!isLargeDelete(diff)) return '';
3964
4067
  var beforeN = diff.summary.beforeNodeCount || 0;
@@ -3970,16 +4073,10 @@ ${os}
3970
4073
  function renderPendingActions(p, stale) {
3971
4074
  if (!canApprove || (p.status !== 'pending' && p.status !== 'conflict')) return '';
3972
4075
  var cached = proposalDiffCache[p.id];
3973
- var warns = proposalWarnings(p, cached);
3974
- var warnNote = warns.length
3975
- ? renderValidationWarnings(warns, {
3976
- title: 'Approve will deploy with ' + warns.length +
3977
- ' configuration warning' + (warns.length === 1 ? '' : 's') +
3978
- ' \u2014 you can finish setup in the editor afterward'
3979
- })
3980
- : '';
3981
- return warnNote +
3982
- '<p class="muted approve-help">Approve deploys this change to Node-RED. Nothing is live until you approve.</p>' +
4076
+ if (proposalHasNoNetChange(cached)) {
4077
+ return '<button type="button" class="action secondary reject-btn" data-id="' + esc(p.id) + '">Reject to close</button>';
4078
+ }
4079
+ return '<p class="muted approve-help">Approve deploys this change to Node-RED. Nothing is live until you approve.</p>' +
3983
4080
  '<div class="row">' +
3984
4081
  '<button type="button" class="action approve-btn" data-id="' + esc(p.id) +
3985
4082
  '"' + (stale ? ' disabled title="Resolve conflicts before approving"' : (!cached ? ' disabled title="Wait for the diff to load"' : '')) + '>Approve &amp; deploy</button>' +
@@ -4002,7 +4099,7 @@ ${os}
4002
4099
  '</details>';
4003
4100
  }
4004
4101
 
4005
- `}a(Ng,"pageClientScriptPartB2Actions");function Fg(){return`
4102
+ `}a(Mg,"pageClientScriptPartB2Actions");function Dg(){return`
4006
4103
  function renderRequestedVia(p) {
4007
4104
  if (!p.createdBy) return '';
4008
4105
  if (p.createdBy.label) {
@@ -4055,7 +4152,7 @@ ${os}
4055
4152
 
4056
4153
  function renderRebaseChip(p, diff) {
4057
4154
  var rebase = (diff && diff.rebase) || p.rebase;
4058
- var count = p.rebase && p.rebase.liveChangedCount;
4155
+ var count = rebase && rebase.liveChangedCount;
4059
4156
  return '<span class="rebase-chip"' + (p.status === 'pending' && rebase && rebase.state === 'merged' ? '' : ' hidden') +
4060
4157
  '>Rebased \xB7 ' + esc(count == null ? 0 : count) + ' live changes</span>';
4061
4158
  }
@@ -4070,18 +4167,15 @@ ${os}
4070
4167
  : '';
4071
4168
  var plain = cached ? plainDiffSentence(cached, flowLabel) : '';
4072
4169
  // Show for any large delete \u2014 including stale \u2014 so owners see the risk.
4073
- var deleteBanner = cached ? renderDeleteBanner(cached) : '';
4170
+ var deleteBanner = cached && !proposalHasNoNetChange(cached) ? renderDeleteBanner(cached) : '';
4074
4171
  // Card-level banner even before diff loads (warnings come from list API).
4075
- var cardWarns = proposalWarnings(p, cached);
4076
4172
  var deployBanner = p.deployPending
4077
4173
  ? '<div class="proposal-warnings" role="status"><p class="proposal-warnings-title">Deploy outcome unknown</p><p>' +
4078
4174
  esc(p.deployPending.queueReleased
4079
4175
  ? 'The approval queue was released. Check the live flow before approving again.'
4080
4176
  : p.deployPending.reason) + '</p></div>'
4081
4177
  : '';
4082
- var warnBanner = deployBanner + renderRestoredSecrets(p) + (cardWarns.length && !cached
4083
- ? renderValidationWarnings(cardWarns)
4084
- : '');
4178
+ var warnBanner = deployBanner + renderReviewWarnings(p, cached);
4085
4179
  var stale = proposalIsStale(p);
4086
4180
  var actions = renderPendingActions(p, stale);
4087
4181
  var changes =
@@ -4100,15 +4194,15 @@ ${os}
4100
4194
  '<p class="proposal-plain-summary" data-plain-for="' + esc(p.id) + '">' +
4101
4195
  (plain ? esc(plain) : '<span class="muted">Loading summary\u2026</span>') + '</p>' +
4102
4196
  deleteBanner +
4103
- warnBanner +
4197
+ '<div class="proposal-review-notes">' + warnBanner + '</div>' +
4104
4198
  reason +
4105
4199
  '<div class="proposal-conflict-host">' + renderConflictPanel(p, cached) + '</div>' +
4106
4200
  changes +
4107
4201
  renderProposalTech(p, cached) +
4108
- actions +
4202
+ '<div class="proposal-actions">' + actions + '</div>' +
4109
4203
  '</article>';
4110
4204
  }
4111
- `}a(Fg,"pageClientScriptPartB2Cards");function Og(){return`
4205
+ `}a(Dg,"pageClientScriptPartB2Cards");function Hg(){return`
4112
4206
  function renderCompactProposal(p) {
4113
4207
  var title = titleForProposalCard(p);
4114
4208
  return '<div class="proposal-card-compact" data-id="' + esc(p.id) +
@@ -4232,7 +4326,7 @@ ${os}
4232
4326
  bindProposalActions(list);
4233
4327
  focusProposalFromHash();
4234
4328
  }
4235
- `}a(Og,"pageClientScriptPartB2CardsList");function $g(){return`
4329
+ `}a(Hg,"pageClientScriptPartB2CardsList");function jg(){return`
4236
4330
  async function refreshFlowLabels() {
4237
4331
  try {
4238
4332
  var data = await api('/flows');
@@ -4274,7 +4368,7 @@ ${os}
4274
4368
  if (id && label) cachedFlowLabels[id] = String(label);
4275
4369
  });
4276
4370
  var sig = JSON.stringify([decidedHasMore, decidedTotal, cachedProposals.map(function (p) {
4277
- return [p.id, p.status, p.updatedAt, p.baseRevStale, p.flowLabel, p.title];
4371
+ return [p.id, p.status, p.updatedAt, p.baseRevChanged, p.flowLabel, p.title];
4278
4372
  })]);
4279
4373
  var unchanged = sameAsLastRender('proposals', sig);
4280
4374
  if (opts.skipRender || (opts.auto && unchanged)) {
@@ -4407,7 +4501,7 @@ ${os}
4407
4501
  replaceLocationHash('#proposals');
4408
4502
  return true;
4409
4503
  }
4410
- `}a($g,"pageClientScriptPartB2Focus");function Mg(){return`
4504
+ `}a(jg,"pageClientScriptPartB2Focus");function Ug(){return`
4411
4505
  function bindDiffModeToggle(host, diff) {
4412
4506
  host.querySelectorAll('[data-diff-mode]').forEach(function (btn) {
4413
4507
  btn.addEventListener('click', function () {
@@ -4429,8 +4523,8 @@ ${os}
4429
4523
  p.rebase = Object.assign({}, p.rebase || {}, diff.rebase);
4430
4524
  p.status = diff.rebase.state === 'conflict' ? 'conflict' : 'pending';
4431
4525
  }
4432
- if (p && typeof diff.baseRevStale === 'boolean') {
4433
- p.baseRevStale = diff.baseRevStale;
4526
+ if (p && typeof diff.baseRevChanged === 'boolean') {
4527
+ p.baseRevChanged = diff.baseRevChanged;
4434
4528
  }
4435
4529
  // Move cards between review and conflict groups when the rebase state changes.
4436
4530
  if (p && (p.status === 'pending' || p.status === 'conflict') && wasStale !== proposalIsStale(p)) {
@@ -4461,8 +4555,10 @@ ${os}
4461
4555
  if (pill) { pill.className = 'status-pill ' + p.status; pill.textContent = p.status === 'conflict' ? 'Conflict' : p.status; }
4462
4556
  var conflictHost = article.querySelector('.proposal-conflict-host');
4463
4557
  if (conflictHost) conflictHost.innerHTML = renderConflictPanel(p, diff);
4464
- var approveBtn = article.querySelector('.approve-btn');
4465
- if (approveBtn) { approveBtn.disabled = proposalIsStale(p); approveBtn.title = approveBtn.disabled ? 'Resolve conflicts before approving' : ''; }
4558
+ var actionHost = article.querySelector('.proposal-actions');
4559
+ if (actionHost) actionHost.innerHTML = renderPendingActions(p, proposalIsStale(p));
4560
+ var notes = article.querySelector('.proposal-review-notes');
4561
+ if (notes && !p.deployPending) notes.innerHTML = renderReviewWarnings(p, diff);
4466
4562
  var chip = article.querySelector('.rebase-chip');
4467
4563
  if (chip) chip.outerHTML = renderRebaseChip(p, diff);
4468
4564
  bindProposalActions(article);
@@ -4472,7 +4568,7 @@ ${os}
4472
4568
 
4473
4569
  // Banner for any large delete (stale or live) \u2014 owners must see the risk.
4474
4570
  var existingBanner = article.querySelector('.delete-banner');
4475
- if (isLargeDelete(diff)) {
4571
+ if (!proposalHasNoNetChange(diff) && isLargeDelete(diff)) {
4476
4572
  var beforeN = diff.summary.beforeNodeCount || 0;
4477
4573
  var removedN = diff.summary.removed || 0;
4478
4574
  var html = 'Deletes ' + removedN + ' of ' + beforeN + ' nodes';
@@ -4531,11 +4627,11 @@ ${os}
4531
4627
  }
4532
4628
  } catch (e) {
4533
4629
  if (host) {
4534
- host.innerHTML = '<p class="muted">Could not load changes: ' +
4630
+ host.innerHTML = '<p class="muted" role="alert">Could not load changes: ' +
4535
4631
  esc(e.message || String(e)) + '</p>';
4536
4632
  }
4537
4633
  }
4538
- }`}a(Mg,"pageClientScriptPartB2Apply");function Dg(){return`
4634
+ }`}a(Ug,"pageClientScriptPartB2Apply");function Bg(){return`
4539
4635
 
4540
4636
  function dialogFocusables(dialog) {
4541
4637
  if (!dialog) return [];
@@ -4638,7 +4734,7 @@ ${os}
4638
4734
  } else if (destructive) {
4639
4735
  heading.textContent = 'Confirm approve & deploy';
4640
4736
  } else if (warns.length) {
4641
- heading.textContent = 'Approve with configuration warnings';
4737
+ heading.textContent = 'Approve with review notes';
4642
4738
  } else {
4643
4739
  heading.textContent = 'Confirm approve & deploy';
4644
4740
  }
@@ -4664,7 +4760,7 @@ ${os}
4664
4760
  msg.className = 'danger-text';
4665
4761
  msg.hidden = false;
4666
4762
  } else if (warns.length) {
4667
- msg.textContent = 'This proposal has configuration gaps (scaffold). You can approve now and finish setup in the editor afterward.';
4763
+ msg.textContent = 'Review the notes below before deploying. Configuration gaps can be completed in the editor.';
4668
4764
  msg.className = 'muted';
4669
4765
  msg.hidden = false;
4670
4766
  } else {
@@ -4678,9 +4774,7 @@ ${os}
4678
4774
  if (warnEl) {
4679
4775
  if (warns.length) {
4680
4776
  warnEl.hidden = false;
4681
- warnEl.innerHTML = renderValidationWarnings(warns, {
4682
- title: 'Configuration warnings'
4683
- });
4777
+ warnEl.innerHTML = renderReviewWarnings(p, diff);
4684
4778
  } else {
4685
4779
  warnEl.hidden = true;
4686
4780
  warnEl.innerHTML = '';
@@ -4782,7 +4876,7 @@ ${os}
4782
4876
  cachedProposals.forEach(function (peer) {
4783
4877
  if (peer.id !== id && (peer.status === 'pending' || peer.status === 'conflict') && peer.expectedRev === expectedRev) {
4784
4878
  delete proposalDiffCache[peer.id];
4785
- peer.baseRevStale = true;
4879
+ peer.baseRevChanged = true;
4786
4880
  }
4787
4881
  });
4788
4882
  await loadProposals({ skipRender: true });
@@ -4830,6 +4924,7 @@ ${os}
4830
4924
  showToast('Review the loaded diff, then click Approve again.', false);
4831
4925
  return;
4832
4926
  }
4927
+ if (proposalHasNoNetChange(diff)) { showToast('Nothing changes in the live flow. Reject this proposal to close it.', true); return; }
4833
4928
  if (needsApproveConfirm(diff, p)) {
4834
4929
  openDeleteConfirmDialog(id, deploymentType, diff);
4835
4930
  return;
@@ -4883,7 +4978,7 @@ ${os}
4883
4978
  throw e;
4884
4979
  }
4885
4980
  }
4886
- `}a(Dg,"pageClientScriptPartC");function Hg(){return`
4981
+ `}a(Bg,"pageClientScriptPartC");function qg(){return`
4887
4982
  var cachedLogs = [];
4888
4983
 
4889
4984
  function renderLogsTable(logs) {
@@ -4965,7 +5060,7 @@ ${os}
4965
5060
  showFlash(flash, e.message || String(e), false);
4966
5061
  }
4967
5062
  }
4968
- `}a(Hg,"pageClientScriptPartCLogs");function jg(){return`
5063
+ `}a(qg,"pageClientScriptPartCLogs");function zg(){return`
4969
5064
  function applyStatus(s) {
4970
5065
  aiOn = !!s.aiFeaturesEnabled;
4971
5066
  canManage = !!(s.capabilities && s.capabilities.manageLinks);
@@ -5028,8 +5123,8 @@ ${os}
5028
5123
  }
5029
5124
 
5030
5125
  $('proposals-hint').textContent = canApprove
5031
- ? 'Review changes from your AI assistant. Each card shows a short id (first 8 hex after prop-; click to copy the full id). Approve & deploy makes them live; Reject leaves the flow unchanged. Resolve conflicts to review the merged change.'
5032
- : 'Approve, reject, and dismiss require full admin permission (*). You can still review proposals. Short ids are the first 8 hex characters after prop- (click to copy the full id).';
5126
+ ? 'Review your AI assistant\u2019s changes. Approve & deploy applies them to Node-RED. Reject closes a proposal without changing the flow.'
5127
+ : 'Review proposals here. A full Node-RED admin can approve or reject them.';
5033
5128
 
5034
5129
  renderLinksManage();
5035
5130
  }
@@ -5038,10 +5133,14 @@ ${os}
5038
5133
  var nav = $('main-nav');
5039
5134
  if (!nav) return;
5040
5135
  function updateStuck() {
5041
- var top = nav.getBoundingClientRect().top;
5136
+ var rect = nav.getBoundingClientRect();
5137
+ if (rect.height > 0) document.documentElement.style.setProperty('--nav-height', (rect.height + 20) + 'px');
5138
+ var top = rect.top;
5042
5139
  nav.classList.toggle('is-stuck', top <= 0);
5043
5140
  }
5044
5141
  window.addEventListener('scroll', updateStuck, { passive: true });
5142
+ window.addEventListener('resize', updateStuck);
5143
+ if (typeof ResizeObserver !== 'undefined') new ResizeObserver(updateStuck).observe(nav);
5045
5144
  updateStuck();
5046
5145
  }
5047
5146
 
@@ -5126,7 +5225,7 @@ ${os}
5126
5225
  });
5127
5226
  }
5128
5227
  }
5129
- `}a(jg,"pageClientScriptPartD");function Ug(){return`
5228
+ `}a(zg,"pageClientScriptPartD");function Wg(){return`
5130
5229
  async function boot() {
5131
5230
  accessToken = readEditorToken();
5132
5231
  if (!accessToken) {
@@ -5230,11 +5329,11 @@ ${os}
5230
5329
  loadTabData(tab);
5231
5330
  });
5232
5331
 
5233
- ${Bg()}
5234
- ${qg()}
5332
+ ${Gg()}
5333
+ ${Kg()}
5235
5334
  boot();
5236
5335
  })();
5237
- `}a(Ug,"pageClientScriptPartDBoot");function Bg(){return`
5336
+ `}a(Wg,"pageClientScriptPartDBoot");function Gg(){return`
5238
5337
  /*
5239
5338
  * Jump links to the editor (#flow/<id>, #node/<id>). A click first asks an
5240
5339
  * already-open editor tab (BroadcastChannel 'node-red-ai-access', answered by
@@ -5299,7 +5398,7 @@ ${qg()}
5299
5398
  ev.stopPropagation();
5300
5399
  revealInEditor(a.getAttribute('data-reveal-kind'), a.getAttribute('data-reveal-id'), a.getAttribute('href'));
5301
5400
  });
5302
- `}a(Bg,"pageClientEditorJumpScript");function qg(){return`
5401
+ `}a(Gg,"pageClientEditorJumpScript");function Kg(){return`
5303
5402
  /*
5304
5403
  * Auto-refresh when a tab left open becomes visible / focused again.
5305
5404
  * Throttled; skipped while a dialog (e.g. the one-time token modal) is open or
@@ -5349,7 +5448,7 @@ ${qg()}
5349
5448
  });
5350
5449
  window.addEventListener('focus', autoRefresh);
5351
5450
 
5352
- `}a(qg,"pageClientAutoRefreshScript");function pc(){return(kg()+Ag()+Sg()+xg()+Tg()+Rg()+Pg()+_g()+Eg()+Cg()+Ig()+Lg()+Ng()+Fg()+Og()+$g()+Mg()+Dg()+Hg()+jg()+uc()+Ug()).trim()}a(pc,"pageClientScript");function zg(){return`
5451
+ `}a(Kg,"pageClientAutoRefreshScript");function wc(){return(Tg()+Rg()+Pg()+_g()+Cg()+Eg()+Ig()+Lg()+Ng()+Fg()+Og()+$g()+Mg()+Dg()+Hg()+jg()+Ug()+Bg()+qg()+zg()+yc()+Wg()).trim()}a(wc,"pageClientScript");function Jg(){return`
5353
5452
  :root {
5354
5453
  color-scheme: light;
5355
5454
  --bg: #eef2f5;
@@ -5377,10 +5476,12 @@ body {
5377
5476
  linear-gradient(165deg, #e8eef3 0%, var(--bg) 55%, #e4ebe6 100%);
5378
5477
  color: var(--ink);
5379
5478
  min-height: 100vh;
5479
+ font-size: 17px;
5480
+ line-height: 1.55;
5380
5481
  }
5381
5482
  main {
5382
5483
  width: 100%;
5383
- max-width: min(1400px, 95vw);
5484
+ max-width: min(1640px, 97vw);
5384
5485
  margin: 0 auto;
5385
5486
  padding: 2.25rem clamp(0.85rem, 2.5vw, 1.75rem) 3.5rem;
5386
5487
  }
@@ -5409,7 +5510,7 @@ h1 {
5409
5510
  /* Signed-out card: same look as the OAuth consent / sign-in page (light only). */
5410
5511
  .auth-gate {
5411
5512
  --sg-bg: #f3f4f6; --sg-card: #fff; --sg-text: #111827; --sg-muted: #4b5563; --sg-line: #e5e7eb;
5412
- --sg-soft: #f9fafb; --sg-brand: #8f0000; --sg-brand-2: #b91c1c; --sg-focus: #2563eb;
5513
+ --sg-soft: #f9fafb; --sg-brand: #0a5c45; --sg-brand-2: #084b38; --sg-focus: #2563eb;
5413
5514
  color-scheme: light;
5414
5515
  position: fixed; inset: 0; z-index: 1000; overflow: auto;
5415
5516
  display: flex; align-items: center; justify-content: center; padding: 24px 16px;
@@ -5428,7 +5529,7 @@ body.signed-out { overflow: hidden; }
5428
5529
  .auth-gate h1 { font-size: 19px; line-height: 1.3; margin: 0 0 6px; color: var(--sg-text); }
5429
5530
  .auth-gate .sg-sub { margin: 0 0 14px; color: var(--sg-muted); font-size: 14px; }
5430
5531
  .auth-gate .sg-status { font-size: 14px; color: var(--sg-muted); margin: 0 0 14px; }
5431
- .auth-gate .sg-status.error { color: var(--sg-brand-2); }
5532
+ .auth-gate .sg-status.error { color: var(--danger); }
5432
5533
  .auth-gate .sg-actions { display: flex; gap: 10px; }
5433
5534
  .auth-gate button { font: inherit; border-radius: 10px; padding: 10px 16px; cursor: pointer; border: 1px solid var(--sg-line); background: var(--sg-card); color: var(--sg-text); }
5434
5535
  .auth-gate button:focus-visible, .auth-gate input:focus-visible, .auth-gate a:focus-visible { outline: 3px solid var(--sg-focus); outline-offset: 2px; }
@@ -5441,16 +5542,17 @@ body.signed-out { overflow: hidden; }
5441
5542
  .auth-gate .sg-login input { font: inherit; padding: 9px 11px; border-radius: 10px; border: 1px solid var(--sg-line); background: var(--sg-soft); color: var(--sg-text); }
5442
5543
  .auth-gate .sg-editor-option { display: inline-block; margin: 10px 0 14px; font-size: 14px; }
5443
5544
  .auth-gate .sg-editor-option[hidden] { display: none; }
5444
- .auth-gate .sg-foot { padding: 12px 22px; border-top: 1px solid var(--sg-line); font-size: 12px; color: var(--sg-muted); background: var(--sg-soft); }
5545
+ .auth-gate .sg-foot { padding: 12px 22px; border-top: 1px solid var(--sg-line); font-size: 14px; font-weight: 500; color: var(--sg-text); background: var(--sg-soft); }
5445
5546
  .auth-gate a { color: var(--sg-focus); }
5446
5547
  @media (max-width: 420px) { .auth-gate .sg-actions { flex-direction: column-reverse; } .auth-gate .sg-secondary { flex: 1; } }
5447
- `}a(zg,"pageStylesPartA");function Wg(){return`
5548
+ `}a(Jg,"pageStylesPartA");function Vg(){return`
5448
5549
  nav.tabs {
5449
5550
  position: sticky;
5450
5551
  top: 0;
5451
5552
  z-index: 40;
5452
5553
  display: flex;
5453
- flex-wrap: wrap;
5554
+ flex-wrap: nowrap;
5555
+ overflow-x: auto;
5454
5556
  gap: 0.4rem;
5455
5557
  margin: 0 0 1.1rem;
5456
5558
  padding: 0.55rem 0;
@@ -5464,6 +5566,7 @@ nav.tabs.is-stuck {
5464
5566
  background: var(--soft);
5465
5567
  }
5466
5568
  nav.tabs button {
5569
+ flex: 0 0 auto;
5467
5570
  border: 1px solid var(--line);
5468
5571
  background: var(--card);
5469
5572
  color: var(--ink);
@@ -5486,7 +5589,7 @@ nav.tabs button[aria-selected="true"] {
5486
5589
  border-radius: 999px;
5487
5590
  background: var(--card);
5488
5591
  color: var(--accent);
5489
- font-size: 0.72rem;
5592
+ font-size: 0.85rem;
5490
5593
  font-weight: 700;
5491
5594
  line-height: 1.25;
5492
5595
  vertical-align: middle;
@@ -5538,7 +5641,7 @@ dd {
5538
5641
  padding: 0.22rem 0.55rem;
5539
5642
  border-radius: 6px;
5540
5643
  cursor: help;
5541
- background: var`}a(Wg,"pageStylesPartA2");function Gg(){return`(--accent-soft);
5644
+ background: var`}a(Vg,"pageStylesPartA2");function Yg(){return`(--accent-soft);
5542
5645
  color: var(--accent);
5543
5646
  font-size: 0.82rem;
5544
5647
  font-weight: 650;
@@ -5575,7 +5678,7 @@ dd {
5575
5678
  display: inline-block;
5576
5679
  padding: 0.12rem 0.45rem;
5577
5680
  border-radius: 4px;
5578
- font-size: 0.78rem;
5681
+ font-size: 0.88rem;
5579
5682
  font-weight: 650;
5580
5683
  white-space: nowrap;
5581
5684
  background: var(--soft);
@@ -5729,7 +5832,7 @@ dialog.token-modal { width: min(720px, 94vw); max-width: min(720px, 94vw); max-h
5729
5832
  color: var(--muted);
5730
5833
  font-size: 0.92rem;
5731
5834
  }
5732
- `}a(Gg,"pageStylesPartB");function Kg(){return`
5835
+ `}a(Yg,"pageStylesPartB");function Xg(){return`
5733
5836
  .logs-toolbar { margin-bottom: 0.65rem; }
5734
5837
  table.audit-table {
5735
5838
  table-layout: fixed;
@@ -5768,7 +5871,7 @@ table.audit-table {
5768
5871
  display: inline-block;
5769
5872
  padding: 0.12rem 0.45rem;
5770
5873
  border-radius: 5px;
5771
- font-size: 0.78rem;
5874
+ font-size: 0.88rem;
5772
5875
  font-weight: 650;
5773
5876
  letter-spacing: 0.01em;
5774
5877
  white-space: nowrap;
@@ -5795,7 +5898,7 @@ table.audit-table {
5795
5898
  word-break: break-word;
5796
5899
  max-height: 10rem;
5797
5900
  overflow: auto;
5798
- font-size: 0.78rem;
5901
+ font-size: 0.88rem;
5799
5902
  background: var(--soft);
5800
5903
  padding: 0.4rem 0.5rem;
5801
5904
  border-radius: 6px;
@@ -5805,7 +5908,7 @@ table.audit-table {
5805
5908
  cursor: help;
5806
5909
  border-bottom: 1px dotted var(--line);
5807
5910
  }
5808
- `}a(Kg,"pageStylesPartC");function Jg(){return`
5911
+ `}a(Xg,"pageStylesPartC");function Zg(){return`
5809
5912
  .proposal-diff {
5810
5913
  margin: 0.65rem 0;
5811
5914
  padding: 0.55rem 0.65rem;
@@ -5882,15 +5985,15 @@ table.audit-table {
5882
5985
  }
5883
5986
  .prop-change-scalar .scalar-side .side-label {
5884
5987
  display: block;
5885
- font-size: 0.75rem;
5988
+ font-size: 0.88rem;
5886
5989
  color: var(--muted);
5887
5990
  margin-bottom: 0.15rem;
5888
5991
  }
5889
- `}a(Jg,"pageStylesPartD");function Vg(){return`
5992
+ `}a(Zg,"pageStylesPartD");function Qg(){return`
5890
5993
  .diff-block {
5891
5994
  margin-top: 0.25rem;
5892
5995
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
5893
- font-size: 0.78rem;
5996
+ font-size: 0.88rem;
5894
5997
  line-height: 1.4;
5895
5998
  overflow-x: hidden;
5896
5999
  overflow-y: auto;
@@ -5946,7 +6049,7 @@ table.audit-table {
5946
6049
  overflow-y: auto;
5947
6050
  max-height: 18rem;
5948
6051
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
5949
- font-size: 0.78rem;
6052
+ font-size: 0.88rem;
5950
6053
  line-height: 1.4;
5951
6054
  background: var(--soft);
5952
6055
  }
@@ -5959,7 +6062,7 @@ table.audit-table {
5959
6062
  z-index: 1;
5960
6063
  background: #eef2f5;
5961
6064
  color: var(--muted);
5962
- font-size: 0.72rem;
6065
+ font-size: 0.85rem;
5963
6066
  font-weight: 650;
5964
6067
  letter-spacing: 0.02em;
5965
6068
  text-transform: uppercase;
@@ -5968,9 +6071,9 @@ table.audit-table {
5968
6071
  }
5969
6072
  .diff-before { color: var(--danger); word-break: break-word; }
5970
6073
  .diff-after { color: var(--ok); word-break: break-word; }
5971
- `}a(Vg,"pageStylesPartE");function Yg(){return`
6074
+ `}a(Qg,"pageStylesPartE");function ey(){return`
5972
6075
  .proposal-detail {
5973
- scroll-margin-top: 4.5rem;
6076
+ scroll-margin-top: var(--nav-height, 6rem);
5974
6077
  }
5975
6078
  .proposal-detail.proposal-highlight {
5976
6079
  outline: 2px solid var(--accent);
@@ -6062,7 +6165,7 @@ table.audit-table {
6062
6165
  overflow: auto;
6063
6166
  max-height: 14rem;
6064
6167
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
6065
- font-size: 0.78rem;
6168
+ font-size: 0.88rem;
6066
6169
  line-height: 1.4;
6067
6170
  background: var(--card);
6068
6171
  border: 1px solid var(--line);
@@ -6094,7 +6197,7 @@ table.audit-table {
6094
6197
  .audit-table .col-action { width: 5.5rem; min-width: 5.5rem; }
6095
6198
  .audit-table .col-actor { width: 7rem; min-width: 6rem; }
6096
6199
  }
6097
- `}a(Yg,"pageStylesPartF");function Xg(){return`
6200
+ `}a(ey,"pageStylesPartF");function ty(){return`
6098
6201
  .dash-grid {
6099
6202
  display: grid;
6100
6203
  grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
@@ -6122,7 +6225,8 @@ table.audit-table {
6122
6225
  }
6123
6226
  .filter-row {
6124
6227
  display: flex;
6125
- flex-wrap: wrap;
6228
+ flex-wrap: nowrap;
6229
+ overflow-x: auto;
6126
6230
  gap: 0.4rem;
6127
6231
  margin: 0 0 0.75rem;
6128
6232
  align-items: center;
@@ -6326,7 +6430,7 @@ button.id-copy:focus {
6326
6430
  background: var(--code-bg, var(--soft));
6327
6431
  border-radius: 4px;
6328
6432
  }
6329
- `}a(Xg,"pageStylesPartG");function Zg(){return`
6433
+ `}a(ty,"pageStylesPartG");function ny(){return`
6330
6434
  .stale-group {
6331
6435
  margin: 1rem 0 0;
6332
6436
  border: 1px solid var(--warn-border);
@@ -6472,7 +6576,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6472
6576
  display: inline-block;
6473
6577
  padding: 0.1rem 0.45rem;
6474
6578
  border-radius: 5px;
6475
- font-size: 0.78rem;
6579
+ font-size: 0.88rem;
6476
6580
  font-weight: 650;
6477
6581
  text-transform: capitalize;
6478
6582
  }
@@ -6490,7 +6594,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6490
6594
  margin-left: auto;
6491
6595
  }
6492
6596
  .debug-toggle input { width: auto; }
6493
- `}a(Zg,"pageStylesPartH");function Qg(){return`
6597
+ `}a(ny,"pageStylesPartH");function ry(){return`
6494
6598
  .card {
6495
6599
  background: var(--card);
6496
6600
  border: 1px solid var(--line);
@@ -6503,12 +6607,26 @@ dialog h3 { margin: 0 0 0.4rem; }
6503
6607
  .card-head h3 { margin: 0; }
6504
6608
  .settings-grid {
6505
6609
  display: grid;
6506
- grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr));
6610
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 540px), 1fr));
6507
6611
  gap: 1rem;
6508
6612
  align-items: start;
6509
6613
  margin-bottom: 1rem;
6510
6614
  }
6511
- .settings-grid .card { margin: 0; }
6615
+ .settings-grid .card { margin: 0; min-width: 0; }
6616
+ #secret-redaction-card { grid-column: 1 / -1; }
6617
+ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
6618
+ .proposal-conflict, .conflict-choice { padding: 1rem; margin: 0.75rem 0; border: 1px solid var(--warn-border); border-radius: 8px; background: var(--warn-bg); }
6619
+ .conflict-option { margin: 0.8rem 0; }
6620
+ .conflict-values dl { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.75rem; }
6621
+ .conflict-values dl > div { min-width: 0; }
6622
+ .conflict-values dd { margin: 0; }
6623
+ .conflict-values pre { max-height: 20rem; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; font-size: 0.92rem; }
6624
+ .review-warnings .proposal-warnings { border: 0; padding: 0; background: transparent; }
6625
+ .no-net-change { padding: 1rem; border: 1px solid var(--line); border-radius: 8px; }
6626
+ .spinner { display: inline-block; width: 1em; height: 1em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: recheck-spin .7s linear infinite; }
6627
+ @keyframes recheck-spin { to { transform: rotate(360deg); } }
6628
+ @media (max-width: 850px) { .conflict-values dl { grid-template-columns: 1fr; } }
6629
+ @media (prefers-reduced-motion: reduce) { .spinner { animation: none; } }
6512
6630
  .form-row { margin: 0 0 0.9rem; }
6513
6631
  .form-row:last-child { margin-bottom: 0; }
6514
6632
  .form-row > label, .form-row > .label { display: block; font-weight: 600; margin: 0 0 0.3rem; }
@@ -6557,10 +6675,11 @@ dialog h3 { margin: 0 0 0.4rem; }
6557
6675
  .conflict-list li { margin: 0.25rem 0; }
6558
6676
  @media (max-width: 640px) {
6559
6677
  nav.tabs { overflow-x: auto; flex-wrap: nowrap; }
6560
- nav.tabs button { flex: 0 0 auto; }
6678
+ nav.tabs button {
6679
+ flex: 0 0 auto; flex: 0 0 auto; }
6561
6680
  .field-line { flex-wrap: wrap; }
6562
6681
  }
6563
- `}a(Qg,"pageStylesDashboard");function fc(){return(zg()+Wg()+Gg()+Kg()+Jg()+Vg()+Yg()+Xg()+Zg()+Qg()).trim()}a(fc,"pageStyles");function ey(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;")}a(ey,"escapeHtml");function ty(){return`
6682
+ `}a(ry,"pageStylesDashboard");function bc(){return(Jg()+Vg()+Yg()+Xg()+Zg()+Qg()+ey()+ty()+ny()+ry()).trim()}a(bc,"pageStyles");function oy(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;")}a(oy,"escapeHtml");function sy(){return`
6564
6683
  <header class="page-header">
6565
6684
  <h1>Node-RED AI Access</h1>
6566
6685
  <p class="lede">
@@ -6575,7 +6694,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6575
6694
  <div id="boot-flash" class="flash" hidden></div>
6576
6695
  <div id="auth-gate" class="auth-gate" hidden>
6577
6696
  <div class="sg-card" role="dialog" aria-modal="true" aria-labelledby="auth-gate-title">
6578
- <div class="sg-head">${Fi}<div class="sg-brand"><b>Node-RED AI Access</b><span>Dashboard</span></div></div>
6697
+ <div class="sg-head">${Ui}<div class="sg-brand"><b>Node-RED AI Access</b><span>Dashboard</span></div></div>
6579
6698
  <div class="sg-body">
6580
6699
  <h1 id="auth-gate-title">Sign in to Node-RED</h1>
6581
6700
  <p class="sg-sub">AI Access uses your Node-RED editor account. Signing in here also signs in the editor in this browser.</p>
@@ -6604,7 +6723,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6604
6723
  Proposals <span class="tab-badge" id="proposals-pending-badge" hidden>0</span>
6605
6724
  </button>
6606
6725
  <button type="button" data-tab="logs" aria-selected="false">Audit log</button>
6607
- </nav>`}a(ty,"renderMcpPageHeader");function ny(){return`
6726
+ </nav>`}a(sy,"renderMcpPageHeader");function iy(){return`
6608
6727
  <section class="panel active" id="section-status" aria-label="Overview" hidden>
6609
6728
  <h2>Overview</h2>
6610
6729
  <div id="status-flash" class="flash" hidden></div>
@@ -6648,7 +6767,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6648
6767
  <div class="row" style="margin-top:1rem">
6649
6768
  <button type="button" class="action secondary" id="refresh-status">Refresh</button>
6650
6769
  </div>
6651
- </section>`}a(ny,"renderMcpPageStatusSection");function ry(){return`
6770
+ </section>`}a(iy,"renderMcpPageStatusSection");function ay(){return`
6652
6771
  <section class="panel" id="section-links" aria-label="Access tokens" hidden>
6653
6772
  <h2>Access tokens</h2>
6654
6773
  <p class="muted" id="links-intro">
@@ -6659,7 +6778,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6659
6778
  <div id="links-flash" class="flash" hidden></div>
6660
6779
  <div id="links-manage"></div>
6661
6780
  <div id="links-list"></div>
6662
- </section>`}a(ry,"renderMcpPageLinksSection");function be(e){return`<span class="lock-badge" id="lock-${e}" hidden>Locked by settings.js</span>`}a(be,"lockBadge");function oy(){return`
6781
+ </section>`}a(ay,"renderMcpPageLinksSection");function be(e){return`<span class="lock-badge" id="lock-${e}" hidden>Locked by settings.js</span>`}a(be,"lockBadge");function dy(){return`
6663
6782
  <section class="panel" id="section-settings" aria-label="Settings" hidden>
6664
6783
  <h2>Settings</h2>
6665
6784
  <p class="muted">Changes save automatically and apply immediately (no restart).</p>
@@ -6696,7 +6815,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6696
6815
  <label for="set-ttl">Default access token expiry</label>
6697
6816
  <div class="field-line">
6698
6817
  <input type="number" id="set-ttl" min="1" step="1" class="num" />
6699
- <span class="unit">hours</span>
6818
+ <span class="unit">hours (minimum 1)</span>
6700
6819
  ${be("ttl")}
6701
6820
  </div>
6702
6821
  </div>
@@ -6794,7 +6913,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6794
6913
  <p class="hint" id="set-trust-proxy-inline"></p>
6795
6914
  </div>
6796
6915
  </div>
6797
- </section>`}a(oy,"renderMcpPageSettingsSection");function sy(){return`
6916
+ </section>`}a(dy,"renderMcpPageSettingsSection");function ly(){return`
6798
6917
  <section class="panel" id="section-oauth" aria-label="OAuth" hidden>
6799
6918
  <h2>MCP OAuth</h2>
6800
6919
  <p class="muted">MCP clients (ChatGPT, Claude, \u2026) can request read or propose access through OAuth.</p>
@@ -6869,7 +6988,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6869
6988
  <button type="button" class="action secondary" id="oauth-refresh">Refresh</button></div>
6870
6989
  <div id="oauth-clients" aria-live="polite"><p class="muted">Loading\u2026</p></div>
6871
6990
  </div>
6872
- </section>`}a(sy,"renderMcpPageOAuthSection");function iy(){return`
6991
+ </section>`}a(ly,"renderMcpPageOAuthSection");function cy(){return`
6873
6992
  <section class="panel" id="section-proposals" aria-label="Proposals" hidden>
6874
6993
  <h2>Proposals</h2>
6875
6994
  <p id="proposals-hint" class="muted"></p>
@@ -6882,12 +7001,12 @@ dialog h3 { margin: 0 0 0.4rem; }
6882
7001
  </button>
6883
7002
  <input type="search" id="proposals-search" class="proposals-search"
6884
7003
  placeholder="Search title or id (e.g. efd159aa)\u2026"
6885
- aria-label="Search proposals by title or short id (first 8 hex after prop-)"
7004
+ aria-label="Search proposals by title or id"
6886
7005
  autocomplete="off" />
6887
7006
  </div>
6888
7007
  <div id="proposals-flash" class="flash" hidden></div>
6889
7008
  <div id="proposals-list"><p class="muted">Loading\u2026</p></div>
6890
- </section>`}a(iy,"renderMcpPageProposalsSection");function ay(){return`
7009
+ </section>`}a(cy,"renderMcpPageProposalsSection");function uy(){return`
6891
7010
  <section class="panel" id="section-logs" aria-label="Audit log" hidden>
6892
7011
  <h2>Audit log</h2>
6893
7012
  <div id="logs-flash" class="flash" hidden></div>
@@ -6903,7 +7022,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6903
7022
  </label>
6904
7023
  </div>
6905
7024
  <div id="logs-list"><p class="muted">Loading\u2026</p></div>
6906
- </section>`}a(ay,"renderMcpPageLogsSection");function dy(){return`
7025
+ </section>`}a(uy,"renderMcpPageLogsSection");function py(){return`
6907
7026
  <dialog id="reject-dialog">
6908
7027
  <form method="dialog" id="reject-form">
6909
7028
  <h3>Reject proposal</h3>
@@ -6945,7 +7064,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6945
7064
  <button type="submit" value="confirm" class="action danger" id="confirm-action-btn">Confirm</button>
6946
7065
  </div>
6947
7066
  </form>
6948
- </dialog>`}a(dy,"renderMcpPageDialogs");function hc(e){let t={apiBase:e.apiBase,mcpRpc:e.mcpRpc??"/mcp/rpc",dashboardPath:e.dashboardPath??"/ai-access",editorRoot:e.editorRoot,authTokenKey:e.authTokenKey};return`<!DOCTYPE html>
7067
+ </dialog>`}a(py,"renderMcpPageDialogs");function vc(e){let t={apiBase:e.apiBase,mcpRpc:e.mcpRpc??"/mcp/rpc",dashboardPath:e.dashboardPath??"/ai-access",editorRoot:e.editorRoot,authTokenKey:e.authTokenKey};return`<!DOCTYPE html>
6949
7068
  <html lang="en">
6950
7069
  <head>
6951
7070
  <meta charset="utf-8" />
@@ -6953,23 +7072,23 @@ dialog h3 { margin: 0 0 0.4rem; }
6953
7072
  <meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" />
6954
7073
  <meta http-equiv="Pragma" content="no-cache" />
6955
7074
  <title>Node-RED AI Access</title>
6956
- <style>${fc()}</style>
7075
+ <style>${bc()}</style>
6957
7076
  </head>
6958
7077
  <body>
6959
7078
  <main
6960
7079
  id="mcp-root"
6961
- data-config="${ey(JSON.stringify(t))}"
6962
- >${ty()}
6963
- ${ny()}
6964
- ${oy()}
6965
- ${ry()}
6966
- ${sy()}
7080
+ data-config="${oy(JSON.stringify(t))}"
7081
+ >${sy()}
6967
7082
  ${iy()}
6968
- ${ay()}
6969
7083
  ${dy()}
7084
+ ${ay()}
7085
+ ${ly()}
7086
+ ${cy()}
7087
+ ${uy()}
7088
+ ${py()}
6970
7089
  </main>
6971
- <script>${pc()}</script>
7090
+ <script>${wc()}</script>
6972
7091
  </body>
6973
- </html>`}a(hc,"renderMcpPage");Fe();et();Te();Je();se();$t();ie();tt();zt();Fe();Io();et();Pe();function Ue(e,t,n,r={}){e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("Content-Type","application/json; charset=utf-8");let o={error:nt[n]??"Request failed",code:n,...r},s=we(n,"rest");s&&typeof o.hint!="string"&&(o.hint=s),e.status(t).json(o)}a(Ue,"sendSafeJsonError");function zi(e){let t={kind:"admin"};return typeof e.user?.username=="string"&&(t.username=e.user.username),t}a(zi,"adminActor");function mc(e,t,n,r,o){return async(s,i)=>{if(!s.accessLink){i.setHeader("WWW-Authenticate",ce),i.status(401).json({error:xe,code:qe});return}try{let d=V(s,r),l=ge(s.query),c=await Tt(e,t,s.accessLink,s.body??{},n,d,o),u=ne(e),p=Ve(e,e.get(c.id),s.accessLink.id),f=c.warnings;i.status(201).json({...fe(c,{...d,include:l}),mine:!0,...Ye(p),...ue(u),...f&&f.length>0?{warnings:f}:{}})}catch(d){let l=W(d,"rest");if(l){i.status(l.statusCode).json(l.body);return}B(i,d);return}}}a(mc,"createProposalHandler");function gc(e,t,n){return async(r,o)=>{let s=await Qe(t),i=s?.rev,d=s?co(s.flows):void 0,l={...V(r,n),...d?{liveFlowLabels:d}:{}},c=ge(r.query),u;try{u=xo(r.query)}catch(x){B(o,x);return}let p=s?$e(e,s).expired:ne(e),f=r.accessLink;if(f){let x=Tn(e,f,l,u),$=x.proposals.map(L=>{let D=he(e,f,L,{...l,include:c},{includeConflicts:!1});return!U(D.status)||i===void 0?D:{...D,baseRevStale:D.expectedRev!==i}});o.json({proposals:$,...Jt({...x,returned:x.proposals.length}),...ue(p)});return}let h={};u.flowId&&(h.flowId=u.flowId);let m=Da(e.list(h).filter(x=>di(x.status,u.status)),{...l,include:c}),g=u.offset??0,A=(u.limit!==void 0?m.slice(g,g+u.limit):g>0?m.slice(g):m).map(x=>!U(x.status)||i===void 0?x:{...x,baseRevStale:x.expectedRev!==i});o.json({proposals:A,...Jt({total:m.length,limit:u.limit??null,offset:g,returned:A.length}),...ue(p)})}}a(gc,"listProposalsHandler");function yc(e,t,n){return async(r,o)=>{let s=r.params.id;if(typeof s!="string"||s.length===0){o.status(400).json({error:"Missing proposal id"});return}let i=V(r,t),d=ge(r.query),l=await Qe(n);try{if(r.accessLink){let c=ct(e,r.accessLink,s,i);o.json(he(e,r.accessLink,c,{...i,include:d},{...l?{live:l}:{}}));return}l?$e(e,l):ne(e),o.json(fe(e.get(s),{...i,include:d}))}catch(c){if(c instanceof me){Ue(o,404,c.code);return}let u=W(c,"rest");if(u){o.status(u.statusCode).json(u.body);return}B(o,c);return}}}a(yc,"getProposalHandler");function wc(e,t){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){r.status(400).json({error:"Missing proposal id"});return}let s=n.body??{},i={};if(s.mergedHash!==void 0){if(typeof s.mergedHash!="string"||!/^[0-9a-f]{8,64}$/.test(s.mergedHash)){r.status(400).json({error:"mergedHash must be a hex string",code:"invalid_body"});return}i.mergedHash=s.mergedHash}if(s.deploymentType!==void 0){if(s.deploymentType!=="nodes"&&s.deploymentType!=="flows"&&s.deploymentType!=="full"){r.status(400).json({error:"deploymentType must be 'nodes', 'flows', or 'full'"});return}i.deploymentType=s.deploymentType}try{let d=await e.approve(o,zi(n),i);r.json(pe(d,V(n,t)))}catch(d){if(d instanceof Ke){Ue(r,409,d.code,{conflicts:d.conflicts});return}if(d instanceof Yt){Ue(r,409,d.code,{mergedHash:d.mergedHash,rebaseState:d.rebaseState});return}if(d instanceof Xt||d instanceof Pt||d instanceof Zt){Ue(r,d.statusCode,d.code);return}if(d instanceof me||d instanceof Y||d instanceof Re||d instanceof le){Ue(r,d.statusCode,d.code);return}B(r,d);return}}}a(wc,"approveProposalHandler");function bc(e,t){return(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){r.status(400).json({error:"Missing proposal id"});return}let s=n.body??{},i=typeof s.reason=="string"?s.reason:"Rejected by admin";try{let d=pe(e.reject(o,zi(n),i),V(n,t));r.json(d)}catch(d){if(d instanceof me||d instanceof Y||d instanceof le){Ue(r,d.statusCode,d.code);return}B(r,d);return}}}a(bc,"rejectProposalHandler");function vc(e,t,n){return(r,o)=>{if(!r.accessLink){o.setHeader("WWW-Authenticate",ce),o.status(401).json({error:xe,code:qe});return}let s=r.params.id;if(typeof s!="string"||s.length===0){o.status(400).json({error:"Missing proposal id",code:"missing_proposal_id"});return}let i=r.body??{},d=V(r,n),l=ge(r.query);try{let c=Rn(e,t,r.accessLink,s,i.reason,d);o.json(he(e,r.accessLink,c,{...d,include:l}))}catch(c){let u=W(c,"rest");if(u){Ue(o,u.statusCode,u.body.code);return}Ue(o,500,"internal_error");return}}}a(vc,"withdrawProposalHandler");function kc(e,t){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){r.status(400).json({error:"Missing proposal id"});return}try{let s=n.accessLink?ct(e,n.accessLink,o):e.get(o),i=In(n.query.format);if(!i.ok){r.status(400).json({error:"Invalid diff format",code:"invalid_diff_format",hint:"Valid formats: summary, full (default summary)."});return}let d=Ln(await $n(s,Xe(n,t),e.get(o)),i.format);r.json(d)}catch(s){if(s instanceof me){Ue(r,404,s.code);return}if(s instanceof X||s instanceof Le||s instanceof ze){Ue(r,s.statusCode,s.code);return}let i=W(s,"rest");if(i){r.status(i.statusCode).json(i.body);return}B(r,s);return}}}a(kc,"getProposalDiffHandler");function Ac(e,t,n,r,o){return async(s,i)=>{if(!s.accessLink){Ue(i,401,"access_link_missing");return}try{let d=V(s,r),l=await To(e,t,s.accessLink,String(s.params.id),s.body??{},n,d,o);i.json(he(e,s.accessLink,l,d))}catch(d){B(i,d)}}}a(Ac,"resolveProposalHandler");function Sc(e,t,n,r){return async(o,s)=>{try{let i=o.body??{},d=await Ld(e,t,zi(o),String(o.params.id),i.nodeId,i.choice,n);s.json(fe(d,V(o,r)))}catch(i){B(s,i)}}}a(Sc,"resolveProposalConflictHandler");function xc(e,t,n){return async(r,o)=>{try{ne(e);let s=e.get(String(r.params.id)),i=await Qe(t);i&&U(s.status)&&(s=Ze(e,s,i).proposal),o.json(fe(s,V(r,n)))}catch(s){B(o,s)}}}a(xc,"recheckProposalHandler");Pe();Te();Te();ro();function ly(e){return e.endsWith("/")?e.slice(0,-1):e}a(ly,"trimTrailingSlash");var as=class{static{a(this,"HttpAdapter")}kind="http";baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=ly(t.baseUrl),this.accessToken=t.accessToken,this.fetchImpl=t.fetch??globalThis.fetch.bind(globalThis)}headers(t){let n=new Headers(t);return n.set("Accept","application/json"),n.set("Node-RED-API-Version","v2"),this.accessToken&&n.set("Authorization",`Bearer ${this.accessToken}`),n}async request(t,n={}){return await this.fetchImpl(`${this.baseUrl}${t}`,{...n,headers:this.headers(n.headers)})}async readJson(t,n){let r=await t.text();if(!t.ok){let o=r;try{let s=JSON.parse(r);o=s.message??s.code??r}catch{}throw t.status===404?new X(n):t.status===409?new te("unknown","unknown",`Admin API ${n}`):t.status===401||t.status===403?new ze(Yr):new Error(`Admin API ${n} failed (${t.status}): ${o}`)}if(!r)return null;try{return JSON.parse(r)}catch{throw new Error(`Admin API ${n} returned non-JSON`)}}async getFlows(){let t=await this.request("/flows"),n=await this.readJson(t,"GET /flows");return or(n,"GET /flows")}async getFlow(t){let n=encodeURIComponent(t),[r,o]=await Promise.all([this.request("/flows"),this.request(`/flow/${n}`)]);if(o.status===404)throw new X(t);let s=await this.readJson(r,"GET /flows"),i=await this.readJson(o,`GET /flow/${t}`);return{rev:or(s,"GET /flows").rev,flow:yn(t,i)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new te(t.expectedRev,r.rev,"updateFlow");let o=no(r.flows,t.flowId,t.flow),s=await this.request("/flows",{method:"POST",headers:{"Content-Type":"application/json","Node-RED-Deployment-Type":n},body:JSON.stringify({flows:o,rev:t.expectedRev})});if(s.status===409){let d=r.rev;try{let l=await s.clone().json();typeof l.current_rev=="string"&&(d=l.current_rev)}catch{}throw new te(t.expectedRev,d,"updateFlow")}let i=await this.readJson(s,"POST /flows");if(!i||typeof i!="object"||typeof i.rev!="string")throw new Error("Admin API POST /flows did not return a rev");return{flowId:t.flowId,rev:i.rev}}};Te();ro();function cy(e){let t=e.runtime?.flows;if(!t?.getFlows||!t.getFlow||!t.setFlows)throw new Error("RED.runtime.flows is missing getFlows, getFlow, or setFlows");return t}a(cy,"requireFlowsApi");function uy(e){if(typeof e=="string")return e;if(typeof e.rev=="string"&&e.rev.length>0)return e.rev;throw new Error("RED.runtime.flows.setFlows did not return a rev")}a(uy,"resolveRev");var ds=class{static{a(this,"RedAdapter")}kind="red";flows;constructor(t){this.flows=cy(t)}async getFlows(){let t=await this.flows.getFlows({});return or(t,"RED.runtime.flows.getFlows()")}async getFlow(t){let n=await this.getFlows(),r;try{r=await this.flows.getFlow({id:t})}catch(o){throw py(o)?new X(t):o}return{rev:n.rev,flow:yn(t,r)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new te(t.expectedRev,r.rev,"updateFlow");let o=no(r.flows,t.flowId,t.flow),s;try{s=await this.flows.setFlows({flows:{flows:o,rev:t.expectedRev},deploymentType:n})}catch(i){if(i&&typeof i=="object"&&"code"in i&&i.code==="version_mismatch"){let d=await this.getFlows();throw new te(t.expectedRev,d.rev,"updateFlow")}throw i}return{flowId:t.flowId,rev:uy(s)}}};function Tc(e){let t=e?.runtime?.flows;return!!(t?.getFlows&&t.getFlow&&t.setFlows)}a(Tc,"canUseRedRuntime");function py(e){if(!e||typeof e!="object")return!1;let t=e;if((typeof t.status=="number"?t.status:t.statusCode)===404||t.code==="not_found"||t.code==="flow_not_found")return!0;let r=typeof t.message=="string"?t.message.toLowerCase():"";return r.includes("not found")||r.includes("unknown flow")}a(py,"isRuntimeFlowNotFound");var Rc=require("module");function Pc(e){if(!e||typeof e!="object")return!1;let t=e;return typeof t.getFlows=="function"&&typeof t.getFlow=="function"&&typeof t.setFlows=="function"}a(Pc,"isFlowsApi");function fy(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Rc.createRequire)(e)}a(fy,"createPluginRequire");function hy(e){try{let t=e("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}a(hy,"isNodeRedRuntimeInitialized");function my(){try{let e=fy();if(!hy(e))return null;let t=e("@node-red/runtime");return Pc(t.flows)?t.flows:null}catch{return null}}a(my,"tryRequireInitializedRuntimeFlows");function _c(e){if(e?.runtime&&Pc(e.runtime.flows))return e;let t=my();return t?{runtime:{flows:t}}:null}a(_c,"resolveRuntimeHost");function Ec(e={}){let t=_c(e.RED??null);if(t&&Tc(t))return new ds(t);let n=e.adminAuthEnabled===!0;if(e.http?.baseUrl){if(n&&!e.http.accessToken)throw new Le("In-process Node-RED flow runtime is unavailable and HTTP Admin API fallback has no credentials while adminAuth is enabled. Ensure the plugin runs inside Node-RED so @node-red/runtime.flows can be used.");return new as(e.http)}throw new Le("No usable Node-RED flow adapter: in-process flows API is unavailable and no HTTP Admin API baseUrl was provided")}a(Ec,"createFlowAdapter");Te();Rs();var cs=require("fs"),Cc=require("path");Zr();se();var gy=/^[a-z][a-z0-9._-]{0,63}$/;function yy(e){if(e.kind==="system")return{kind:"system"};if(e.kind==="access-link"){let n={kind:"access-link",linkId:String(e.linkId).replace(/\0/g,"").slice(0,200)};return typeof e.label=="string"&&(n.label=e.label.replace(/\0/g,"").slice(0,200)),n}let t={kind:"admin"};return typeof e.username=="string"&&(t.username=e.username.replace(/\0/g,"").slice(0,200)),t}a(yy,"sanitizeActor");var ls=class{constructor(t,n={}){this.paths=t;this.filePath=(0,Cc.join)(t.logs,"audit.jsonl"),this.now=n.now??(()=>Date.now()),this.maxAppendBytes=n.maxAppendBytes??Vi}static{a(this,"AuditLog")}filePath;now;maxAppendBytes;seq=0;append(t,n,r){let o=gy.test(t)?t:"unknown";this.seq+=1;let s={id:`log-${this.now()}-${this.seq}`,at:new Date(this.now()).toISOString(),action:o,actor:yy(n)};r!==void 0&&(s.details=ha(r));let i=ft(s,this.maxAppendBytes,{kind:"audit"});return Xi(this.paths.root,this.filePath,i),s}list(t=100){if(!(0,cs.existsSync)(this.filePath))return[];let n=(0,cs.readFileSync)(this.filePath,"utf8").split(`
6974
- `).map(o=>o.trim()).filter(Boolean),r=[];for(let o of n)try{r.push(JSON.parse(o))}catch{}return r.slice(-Math.max(1,t)).reverse()}};Je();qr();ve();qt();Io();ke();function wy(e){let t=a(()=>Promise.reject(new ze(e)),"fail");return{kind:"http",getFlows:t,getFlow:t,updateFlow:t}}a(wy,"misconfiguredAdapter");function Ic(e){let t=e.settings,n=typeof t.uiPort=="number"?t.uiPort:1880,r=t.httpAdminRoot??"/",o=!r||r==="/"?"":r.replace(/\/$/,"");return`http://127.0.0.1:${n}${o}`}a(Ic,"localAdminBaseUrl");function us(e,t={}){let n=Mr(e.settings.userDir);Zi(n);let r=t.settingsStore??new fn(n,e.settings.aiAccess,u=>e.log?.warn?.(u));t.bindSettings!==!1&&Wr(r),Kr(r.getEffective().publicBaseUrl);let o=t.linkStore??rs(e);o.configureDefaults({defaultTtlSeconds:r.getEffective().defaultLinkTtlSeconds,maxActiveLinks:r.getEffective().maxActiveLinks});let s=new Qr(n),i=new ls(n),d;if(t.flowAdapter)d=t.flowAdapter;else try{d=Ec({RED:e,http:{baseUrl:Ic(e)},adminAuthEnabled:oe(e.settings)})}catch(u){let p=u instanceof Le?u.message:Yr;e.log?.warn?.(`AI Access flow adapter unavailable: ${p}`),d=wy(p)}d=wa(d);let l;if(t.nodeTypesAdapter)l=t.nodeTypesAdapter;else if(t.flowAdapter)l=Ls();else try{l=Pa({baseUrl:Ic(e)})}catch(u){let p=u instanceof Error?u.message:String(u);e.log?.warn?.(`AI Access node-types adapter unavailable: ${p}`),l=Ls([])}let c=new Co(s,i,d,()=>Ee(e.settings).defaultDeploymentType,e.settings.aiAccess);return{linkStore:o,proposalStore:s,auditLog:i,flowAdapter:d,nodeTypesAdapter:l,proposalService:c,settingsStore:r,...t.catalogueFetcher?{catalogueFetcher:t.catalogueFetcher}:{},...t.catalogueUrl?{catalogueUrl:t.catalogueUrl}:{}}}a(us,"createPluginServices");se();ve();Be();Dr();var Lc=zn(require("dns"),1),Nc=zn(require("http"),1),Fc=zn(require("https"),1),Nr=require("net");ve();function by(e,t){return e===10||e===172&&t>=16&&t<=31||e===192&&t===168}a(by,"isPrivateV4");function vy(e,t){return e===169&&t===254}a(vy,"isLinkLocalV4");function ky(e,t,n){return e===0||e===100&&t>=64&&t<=127||e>=224||e===192&&t===0&&(n===0||n===2)||e===198&&(t===18||t===19||t===51&&n===100)||e===203&&t===0&&n===113}a(ky,"isSpecialUseV4");function Ay(e){let[t=0,n=0,r=0]=e.split(".").map(Number);return t===127?"loopback":by(t,n)||vy(t,n)||ky(t,n,r)||e==="168.63.129.16"?"blocked":"public"}a(Ay,"classifyIpv4");function Sy(e){let t=e.split(":"),n=t.indexOf(""),r=[];for(let o of t){if(!o)continue;if(o.includes(".")){let i=o.split(".").map(Number);if(i.length!==4||i.some(d=>!Number.isInteger(d)||d<0||d>255))return;r.push((i[0]??0)<<8|(i[1]??0)),r.push((i[2]??0)<<8|(i[3]??0));continue}let s=Number.parseInt(o,16);if(!/^[\da-f]{1,4}$/i.test(o)||!Number.isInteger(s))return;r.push(s)}if(n>=0){let o=8-r.length;if(o<1)return;let s=t.slice(0,n).filter(Boolean).length;r.splice(s,0,...Array(o).fill(0))}else if(r.length!==8)return;return r.length===8?r:void 0}a(Sy,"expandIpv6");function xy(e){let t=Sy(e);if(!t)return"blocked";if(t.slice(0,7).every(s=>s===0)&&t[7]===1)return"loopback";let n=t[0]??0,r=t[1]??0;return n===0||n<8192||n>16383||n===8193&&r<=511||n===8193&&r===3512||n===16383&&r<=4095||n===8194?"blocked":"public"}a(xy,"classifyIpv6");function Fr(e){let t=Mt(e);return t?(0,Nr.isIP)(t)===4?Ay(t):xy(t):"blocked"}a(Fr,"classifyPublicUrlAddress");function Ty(e){let t=e.toLowerCase().replace(/\.$/,"");return["metadata","metadata.google.internal","metadata.google.com","metadata.goog","instance-data","instance-data.ec2.internal","metadata.azure.internal","metadata.aws.internal"].some(n=>t===n||t.endsWith(`.${n}`))}a(Ty,"isMetadataHostname");async function Oc(e,t,n){let r=hn(e);if(!r)throw new Error("publicBaseUrl is required for Check");let o=new URL(r),s=o.hostname.replace(/^\[|\]$/g,"");if(Ty(s))throw new Error("Public URL Check cannot target metadata hosts");let i=Mt(s),l=(typeof e=="string"?/^https?:\/\/(\[[^\]]+\]|[^/?#:]+)/i.exec(e.trim())?.[1]:void 0)?.replace(/^\[|\]$/g,"");if(i&&l&&l.toLowerCase()!==i.toLowerCase())throw new Error("Public URL Check requires canonical IP address notation");let c=s.toLowerCase().replace(/\.$/,"")==="localhost"||i!==void 0&&Fr(i)==="loopback",u=Number(o.port||(o.protocol==="https:"?443:80));if(c&&u!==t)throw new Error("Public URL Check may only target this Node-RED instance's port for loopback URLs");let p=a(g=>{let y=Fr(g);if(y==="blocked"||y==="loopback"&&!c||c&&y!=="loopback")throw new Error("Public URL Check cannot target private or non-public addresses")},"validateAddress");if(i)return p(i),{origin:r,address:i,family:(0,Nr.isIP)(i)};let f=await new Promise((g,y)=>{let A=a(()=>y(n.reason instanceof Error?n.reason:new Error(String(n.reason))),"abort");n.throwIfAborted(),n.addEventListener("abort",A,{once:!0}),Lc.default.lookup(s,{all:!0},(x,$)=>{n.removeEventListener("abort",A),x?y(x):g($)})});if(n.throwIfAborted(),!f.length)throw new Error("Public URL Check hostname has no addresses");for(let g of f)p(g.address);let h=[];for(let g of f){let y=Mt(g.address);if(!y)throw new Error("Public URL Check returned an invalid address");h.some(A=>A.address===y)||h.push({address:y,family:(0,Nr.isIP)(y)})}c&&h.sort((g,y)=>g.family-y.family);let m=h[0];if(!m)throw new Error("Public URL Check hostname has no addresses");return{origin:r,...m,addresses:h}}a(Oc,"preparePublicUrlCheck");function Ry(e,t,n){let r=e.hostname.replace(/^\[|\]$/g,"");return new Promise((o,s)=>{let d=(e.protocol==="https:"?Fc.default:Nc.default).request(e,{method:"GET",agent:!1,signal:n,headers:{Host:e.host},...(0,Nr.isIP)(r)?{}:{servername:r},lookup:a((l,c,u)=>{c.all?u(null,[{address:t.address,family:t.family}]):u(null,t.address,t.family)},"lookup")},l=>{o(l.statusCode??0),l.destroy()});d.on("error",s),d.end()})}a(Ry,"requestPinned");async function $c(e,t,n){let r=new URL(e.origin+t),o=e.addresses?.length?e.addresses:[{address:e.address,family:e.family}],s;for(let i of o){n.throwIfAborted();try{return await Ry(r,i,n)}catch(d){if(n.aborted)throw d;s=d}}throw s instanceof Error?s:new Error("Public URL Check request failed")}a($c,"requestPublicUrlCheck");un();ke();function Py(e){return Zn(e.settings.httpAdminRoot)}a(Py,"mcpPageUrl");function _y(e){return typeof e=="boolean"||typeof e=="number"||typeof e=="string"?e:Array.isArray(e)?e.join(", "):typeof e=="function"?"custom function":!1}a(_y,"normalizeTrustProxy");function Hc(e){if(!e)return"Set a Public URL before enabling OAuth.";let t=new URL(e),n=Mt(t.hostname),r=t.hostname.toLowerCase().replace(/\.$/,"")==="localhost"||n==="::1"||n?.startsWith("127.");return t.protocol==="http:"&&!r?"OAuth Public URL uses HTTP; HTTPS is recommended.":null}a(Hc,"oauthPublicUrlWarning");function jc(e,t,n){let{sources:r,...o}=t.getEffective(),{permission:s}=Gr(e,n);return{ok:!0,adminAuthEnabled:oe(e.settings),canEdit:s==="*",permission:s,mcpPageUrl:Py(e),apiBase:Se(e.settings.httpAdminRoot),settings:o,oauthPublicUrlWarning:Hc(o.publicBaseUrl),sources:r,disk:t.getDisk(),trustProxy:_y(n.app.get("trust proxy")),precedence:["settings.js","disk","default"],defaults:{secretNamePatterns:[...Ft]}}}a(jc,"settingsResponse");function Uc(e,t){return(n,r)=>{r.json(jc(e,t,n))}}a(Uc,"getSettingsHandler");function Mc(e,t,n){let r=e[t];if(r!==void 0){if(typeof r!="boolean")throw new Error(`${t} must be a boolean`);n[t]=r}}a(Mc,"parseBooleanField");function Dc(e,t,n){let r=e[t];if(r!==void 0){if(typeof r!="number"||!Number.isFinite(r)||r<=0)throw new Error(`${t} must be a positive number`);n[t]=Math.floor(r)}}a(Dc,"parsePositiveIntField");function Ey(e,t,n){let r=e[t];if(r===void 0)return;let o=t==="rateLimitWindowMs"?100:1,s=t==="rateLimitWindowMs"?36e5:1e6;if(typeof r!="number"||!Number.isInteger(r)||r<o||r>s)throw new Error(`${t} must be an integer between ${o} and ${s}`);n[t]=r}a(Ey,"parseRateLimitField");function Cy(e,t){if(e.corsAllowedOrigins===void 0)return;if(!Array.isArray(e.corsAllowedOrigins))throw new Error("corsAllowedOrigins must be a list of HTTP(S) origins");let n=e.corsAllowedOrigins.map(r=>Kn(r));if(n.some(r=>r===void 0))throw new Error("corsAllowedOrigins must contain only HTTP(S) origins without paths, credentials, queries, or fragments");t.corsAllowedOrigins=[...new Set(n)]}a(Cy,"parseCorsField");function Iy(e,t,n){let r=e.secretNamePatterns;if(r!==void 0){if(r===null){n.push("secretNamePatterns");return}if(!Array.isArray(r)||r.some(o=>typeof o!="string"))throw new Error("secretNamePatterns must be a list of strings (or null to reset)");if(r.length>Jn)throw new Error(`secretNamePatterns allows at most ${Jn} entries`);for(let o of r)if(!Hr.test(o.trim()))throw new Error('secretNamePatterns entries must be 2-40 letters, digits, "_" or "-" (e.g. API_KEY)');t.secretNamePatterns=Vn(r)??[]}}a(Iy,"parseSecretPatternsField");function Bc(e,t,n){return(r,o)=>{let s=r.body;if(!s||typeof s!="object"||Array.isArray(s)){o.status(400).json({error:"Request body must be a settings object",code:"invalid_body"});return}let i={...s};for(let[c,u]of Object.entries(t.getEffective().sources))u==="settings.js"&&delete i[c];t.getEffective().sources.oauth==="settings.js"&&delete i.oauth;let d={},l=[];try{if(i.proposalTtlDays!==void 0){let c=i.proposalTtlDays;if(typeof c!="number"||!Number.isInteger(c)||c<1||c>365)throw new Error("proposalTtlDays must be an integer between 1 and 365");d.proposalTtlDays=c}if(i.oauth!==void 0){if(!i.oauth||typeof i.oauth!="object"||Array.isArray(i.oauth))throw new Error("oauth must be an object");let c=i.oauth,u={};if(c.enabled!==void 0){if(typeof c.enabled!="boolean")throw new Error("oauth.enabled must be a boolean");u.enabled=c.enabled}for(let f of["accessTokenTtlSec","refreshIdleTtlSec","dcrMaxClients"])if(c[f]!==void 0){if(typeof c[f]!="number"||!Number.isInteger(c[f])||c[f]<1||c[f]>31536e3)throw new Error(`oauth.${f} must be a positive integer`);u[f]=c[f]}let p=c.refreshMaxLifetimeSec;if(p!==void 0){if(typeof p!="number"||!Number.isInteger(p)||p<0||p>31536e4)throw new Error("oauth.refreshMaxLifetimeSec must be a non-negative integer");u.refreshMaxLifetimeSec=p}d.oauth=u}Mc(i,"enabled",d),Mc(i,"allowWithoutAdminAuth",d),Dc(i,"defaultLinkTtlSeconds",d),Dc(i,"maxActiveLinks",d)}catch(c){o.status(400).json({error:c.message,code:"invalid_body"});return}if(i.defaultDeploymentType!==void 0){let c=i.defaultDeploymentType;if(c!=="nodes"&&c!=="flows"&&c!=="full"){o.status(400).json({error:"defaultDeploymentType must be 'nodes', 'flows', or 'full'",code:"invalid_body"});return}d.defaultDeploymentType=c}try{i.publicBaseUrl!==void 0&&(d.publicBaseUrl=hn(i.publicBaseUrl)),Cy(i,d),Iy(i,d,l);for(let c of["rateLimitWindowMs","rateLimitMaxPerIp","rateLimitMaxPerToken"])Ey(i,c,d)}catch(c){o.status(400).json({error:c.message,code:"invalid_body"});return}try{let c=t.patch(d,l);Kr(c.publicBaseUrl),n.configureDefaults({defaultTtlSeconds:c.defaultLinkTtlSeconds,maxActiveLinks:c.maxActiveLinks});let u=jc(e,t,r),p=c.publicBaseUrl?new URL(c.publicBaseUrl):void 0,f=p?Mt(p.hostname.replace(/^\[|\]$/g,"")):void 0,h=f?Fr(f):void 0,m=[];h!==void 0&&h!=="public"&&m.push("Public URL uses a private or non-public IP address.");let g=Hc(c.publicBaseUrl);g&&c.publicBaseUrl&&m.push(g),o.json({...u,...m.length?{warnings:m}:{}})}catch(c){if(c instanceof le){o.status(c.statusCode).json({error:c.message,code:c.code});return}B(o,c);return}}}a(Bc,"putSettingsHandler");function qc(e){return async(t,n)=>{let r=new AbortController,o=setTimeout(()=>r.abort(),5e3),s;try{let i;try{let c=e.settings,p=e.server?.address(),f=t.body?.publicBaseUrl,h=typeof f=="string"?new URL(f.trim()).hostname:"";if((h.toLowerCase().replace(/^\[|\]$/g,"").replace(/\.$/,"")==="localhost"||Fr(h.replace(/^\[|\]$/g,""))==="loopback")&&typeof p=="string")throw new Error("Public URL Check does not support loopback URLs when Node-RED listens on a Unix socket");let g=p&&typeof p=="object"?p.port:c.uiPort??1880;i=await Oc(t.body?.publicBaseUrl,g,r.signal)}catch(c){if(r.signal.aborted)throw c;n.status(400).json({error:c.message,code:"invalid_body"});return}s=i.origin+Se(e.settings.httpAdminRoot);let d=await $c(i,Se(e.settings.httpAdminRoot),r.signal),l={status:d,ok:d>=200&&d<300};n.json({ok:l.ok,status:l.status,url:s,message:l.ok?"Discovery endpoint is reachable":`Discovery endpoint returned HTTP ${l.status}`})}catch{n.json({ok:!1,status:null,url:s,error:r.signal.aborted?"Public URL check timed out after 5 seconds":"Could not reach the discovery endpoint"})}finally{clearTimeout(o)}}}a(qc,"checkPublicUrlHandler");ke();$t();ke();ie();qr();var Ly="com55-ai-access",ps=new WeakMap;function Wc(e){let t=new Set;for(let n of String(e??"").split(",")){let[r,...o]=n.split(";").map(d=>d.trim().toLowerCase());if(!r)continue;let s=o.find(d=>d.startsWith("q=")),i=s?Number(s.slice(2)):1;Number.isFinite(i)&&i>0&&t.add(r)}return t}a(Wc,"acceptedMediaTypes");function Gc(e){let t=Wc(e.headers.accept);return t.has("text/event-stream")&&!t.has("text/html")}a(Gc,"isMcpStreamRequest");function Ny(e,t,n){e.httpAdmin.get("/mcp/api/v1",t,n,mi(e)),e.httpAdmin.get("/mcp/api/v1/",t,n,mi(e)),e.httpAdmin.get("/mcp/api/v1/openapi.yaml",t,n,lc()),e.httpAdmin.get("/mcp/api/v1/openapi.json",t,n,cc()),e.httpAdmin.get("/mcp/editor/ai-access-sidebar.js",t,(r,o)=>{o.setHeader("Cache-Control","no-store, no-cache, must-revalidate, max-age=0"),o.setHeader("Pragma","no-cache"),o.setHeader("Expires","0"),o.setHeader("X-AI-Access-Sidebar-Version",Cn),o.type("application/javascript").status(200).send(Lo())})}a(Ny,"registerMcpShellAndDiscovery");var Fy=/^\/mcp\/(?:api\/v1\/proposals|rpc$|a\/[^/]+\/(?:proposals|propose|draft))/;function Kc(e,t={}){let n=us(e,{...t,linkStore:t.linkStore??rs(e)});gd(()=>n.settingsStore.getEffective().proposalTtlDays),ps.get(e)?.();let r=wd(()=>wr(n.proposalStore,n.flowAdapter),500),o=a(()=>{r.cancel(),e.events?.removeListener?.("flows:started",r.trigger)},"cleanup");ps.set(e,o),e.events?.on("flows:started",r.trigger);let s=Qd(()=>n.settingsStore.getEffective()),{ip:i,token:d}=s,l=new ss(Mr(e.settings.userDir),()=>n.settingsStore.getEffective().oauth.dcrMaxClients),c=a(()=>n.settingsStore.getEffective().publicBaseUrl,"publicBase"),u=a(()=>`${c().replace(/\/$/,"")}/mcp`,"mcpUrl"),p=new is(l,u,()=>n.settingsStore.getEffective().oauth.accessTokenTtlSec,()=>n.settingsStore.getEffective().oauth.refreshIdleTtlSec,()=>n.settingsStore.getEffective().oauth.refreshMaxLifetimeSec),f=sc(e,p,c,u);ea(e.httpAdmin,Qi(()=>n.settingsStore.getEffective().corsAllowedOrigins));let h=a(v=>(Array.isArray(v)?v:v?[v]:[]).flatMap(S=>{if(typeof S=="string")return[S];let N=S;return N&&typeof N.path=="string"?[{path:N.path,root:typeof N.root=="string"?N.root:void 0}]:[]}),"staticMounts"),m={authorization:[],metadata:[],details:[],httpNodeRootFallback:e.settings.httpNodeRoot===!1||!e.httpNode},g="",y=a(()=>{let v=oc({httpAdmin:e.httpAdmin,httpNode:e.httpNode,httpAdminRoot:e.settings.httpAdminRoot,httpNodeRoot:e.settings.httpNodeRoot,metadataFallback:m.httpNodeRootFallback,eachNode:e.nodes?I=>e.nodes?.eachNode(I):void 0,staticDirs:h(e.settings.httpStatic),httpStaticRoot:e.settings.httpStaticRoot,fileExists:zc.existsSync}),S=v.authorization,N=v.metadata;m.authorization=S.map(ji),m.metadata=N.map(ji),m.details=[...S.map(I=>({...I,kind:"authorization"})),...N.map(I=>({...I,kind:"metadata"}))];let w=[...m.authorization.map(I=>`${I} \u2014 OAuth authorization routes disabled`),...m.metadata.map(I=>`${I} \u2014 OAuth metadata disabled`)],F=w.join(`
6975
- `);if(F!==g)for(let I of w)e.log?.warn?.(`AI Access OAuth route collision: ${I}`);g=F},"refreshCollisions");y(),e.events?.on("flows:started",y);let A=bi({windowMs:6e4,maxPerIp:30}),x=a((v,S,N)=>{if(!n.settingsStore.getEffective().oauth.enabled||m.authorization.length||!v.path.startsWith("/mcp/oauth/")){N();return}let w=v.path.replace(/\/+$/,"").toLowerCase(),F=a(()=>mn(e)(v,S,()=>f.server(v,S,N)),"run");w==="/mcp/oauth/authorize"?i(v,S,()=>A(v,S,F)):w.startsWith("/mcp/oauth/consent/")?i(v,S,F):F()},"oauthServerMiddleware");x.oauthOwned=!0,e.httpAdmin.use(x);let $=a((v,S,N)=>{if(!n.settingsStore.getEffective().oauth.enabled||m.metadata.length){N();return}f.metadata(v,S,N)},"metadataHandler");$.oauthOwned=!0,m.httpNodeRootFallback?e.httpAdmin.use("/mcp",$):e.httpNode?.use($),e.httpAdmin.get("/mcp/api/v1/oauth/clients",i,d,Ie(e,"api"),(v,S)=>S.json({clients:l.data.clients.map(N=>({client_id:N.client_id,client_name:N.client_name,redirect_uris:N.redirect_uris,lastUsedAt:N.lastUsedAt}))})),e.httpAdmin.get("/mcp/api/v1/oauth/status",i,d,Ie(e,"api"),(v,S)=>{let N=!oe(e.settings)||mt(v)==="*";S.json({...m,details:N?m.details:m.details.map(({file:w,...F})=>F),collisions:[...m.authorization,...m.metadata],oauthEnabled:n.settingsStore.getEffective().oauth.enabled})}),e.httpAdmin.delete("/mcp/api/v1/oauth/clients/:id",i,d,Ie(e,"api"),Qn(e),(v,S)=>S.json({revoked:l.revokeClient(String(v.params.id))})),e.httpAdmin.use((v,S,N)=>{if(!Fy.test(v.path)){N();return}n.flowAdapter.getFlows().then(w=>$a(v,co(w.flows))).catch(()=>{}).finally(()=>N())}),e.httpAdmin.use(Ul(n.auditLog)),Al(e,{linkStore:n.linkStore,proposalStore:n.proposalStore,auditLog:n.auditLog,flowAdapter:kt(n.flowAdapter),nodeTypesAdapter:n.nodeTypesAdapter,...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{},rateLimiters:s}),e.httpAdmin.get("/ai-access",i,d,(v,S)=>{S.append("Content-Security-Policy","form-action 'none'; frame-ancestors 'none'"),S.setHeader("X-Frame-Options","DENY"),S.setHeader("Cache-Control","no-store, no-cache, must-revalidate, max-age=0"),S.setHeader("Pragma","no-cache"),S.setHeader("Expires","0"),S.type("html").status(200).send(hc({apiBase:Se(e.settings.httpAdminRoot),mcpRpc:ht(e.settings.httpAdminRoot),editorRoot:Ae(e.settings.httpAdminRoot),authTokenKey:Vr(e.settings.httpAdminRoot),dashboardPath:Zn(e.settings.httpAdminRoot)}))}),e.httpAdmin.get("/mcp",i,(v,S,N)=>{if(Gc(v)){N();return}S.setHeader("Cache-Control","no-store"),S.redirect(302,Zn(e.settings.httpAdminRoot))}),Ny(e,i,d),e.httpAdmin.get("/mcp/api/v1/status",i,d,ia(e,v=>n.linkStore.validateResult(v)),O((v,S)=>{S.json(Mn(e,v))})),e.httpAdmin.get("/mcp/api/v1/settings",i,d,Ie(e,"api"),O(Uc(e,n.settingsStore))),e.httpAdmin.put("/mcp/api/v1/settings",i,d,Ie(e,"api"),Qn(e),De("rest"),O(Bc(e,n.settingsStore,n.linkStore))),e.httpAdmin.post("/mcp/api/v1/settings/check-public-url",i,d,Ie(e,"api"),Qn(e),De("rest"),O(qc(e)));let L=[i,d,Ie(e,"api"),mn(e),Qn(e)];e.httpAdmin.get("/mcp/api/v1/links",...L,O(Il(e,n.linkStore))),e.httpAdmin.post("/mcp/api/v1/links",...L,O(Ll(e,n.linkStore,n.auditLog))),e.httpAdmin.delete("/mcp/api/v1/links/:id",...L,O(Nl(e,n.linkStore,n.auditLog)));let D=[i,d,mn(e),sa(e,v=>n.linkStore.validateResult(v))],H=n.flowAdapter,T=n.nodeTypesAdapter,k={nodeTypes:T,...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{}};e.httpAdmin.get("/mcp/api/v1/flows",...D,O(qd(H))),e.httpAdmin.get("/mcp/api/v1/flows/:id",...D,O(zo(H))),e.httpAdmin.get("/mcp/api/v1/nodes/:id",...D,O(Wo(H))),e.httpAdmin.get("/mcp/api/v1/nodes/:id/field/:name",...D,O(Go(H))),e.httpAdmin.get("/mcp/api/v1/search",...D,O(Ko(H))),e.httpAdmin.get("/mcp/api/v1/node-types",...D,O(Jo(k))),e.httpAdmin.get("/mcp/api/v1/node-types/:type",...D,O(Vo(k))),e.httpAdmin.get("/mcp/api/v1/palette/search",...D,O(Yo(k)));let _=e.settings.httpAdminRoot;e.httpAdmin.get("/mcp/api/v1/proposals",...D,O(gc(n.proposalStore,n.flowAdapter,_))),e.httpAdmin.get("/mcp/api/v1/proposals/:id",...D,O(yc(n.proposalStore,_,n.flowAdapter))),e.httpAdmin.get("/mcp/api/v1/proposals/:id/diff",...D,O(kc(n.proposalStore,n.flowAdapter))),e.httpAdmin.post("/mcp/api/v1/proposals",i,d,De("rest"),Dt(e,n.linkStore,"rest"),O(mc(n.proposalStore,n.auditLog,n.flowAdapter,_,n.nodeTypesAdapter))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/approve",...L,De("rest"),O(wc(n.proposalService,_))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/reject",...L,De("rest"),O(bc(n.proposalService,_))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/withdraw",i,d,De("rest"),Dt(e,n.linkStore,"rest"),O(vc(n.proposalStore,n.auditLog,_))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/resolve-conflict",...L,De("rest"),O(Sc(n.proposalStore,n.auditLog,H,_))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/recheck",...L,O(xc(n.proposalStore,H,_))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/resolve",i,d,De("rest"),Dt(e,n.linkStore,"rest"),O(Ac(n.proposalStore,n.auditLog,H,_,T)));let E=[i,d,(v,S,N)=>{let w=v.headers.authorization;if(!n.settingsStore.getEffective().oauth.enabled||w?.toLowerCase().startsWith("bearer aia_")){if(n.settingsStore.getEffective().oauth.enabled){let F=S.setHeader.bind(S);S.setHeader=((I,C)=>{if(I.toLowerCase()==="www-authenticate"&&typeof C=="string"&&!C.includes("resource_metadata=")){let ee=`${c().replace(/\/$/,"")}${m.httpNodeRootFallback?"/mcp":""}/.well-known/oauth-protected-resource/mcp`;C+=`, resource_metadata="${ee}", scope="read propose"`}return F(I,C)})}Dt(e,n.linkStore,"mcp")(v,S,N);return}mn(e)(v,S,()=>f.auth(v,S,N))}];e.httpAdmin.post("/mcp/rpc",...E,De("mcp"),O(Ni(e,n))),e.httpAdmin.get("/mcp/rpc",...E,Cr),e.httpAdmin.delete("/mcp/rpc",...E,Cr),e.httpAdmin.post("/mcp",...E,De("mcp"),O(Ni(e,n))),e.httpAdmin.get("/mcp",...E,Cr),e.httpAdmin.delete("/mcp",...E,Cr),e.httpAdmin.get("/mcp/api/v1/logs",i,d,Ie(e,"api"),mn(e),O(Fl(n.auditLog,n.linkStore))),e.httpAdmin.use("/mcp/api/v1",jd()),Rl(e)}a(Kc,"registerRoutes");function Oy(e){e.plugins.registerPlugin(Ly,{type:"com55-ai-access",onremove(){ps.get(e)?.(),ps.delete(e)},onadd(){Kc(e),e.log?.info?.("Node-RED AI Access plugin loaded (dashboard /ai-access, MCP /mcp)")}})}a(Oy,"registerAiAccessPlugin");var $y=Oy;0&&(module.exports={ACCESS_LINK_INVALID_MESSAGE,ACCESS_LINK_MISSING_MESSAGE,AccessLinkStore,EDITOR_SIDEBAR_ASSET_VERSION,MAX_PROPOSAL_JSON_BYTES,SettingsStore,acceptedMediaTypes,areAiFeaturesEnabled,bindSettingsStore,createPluginServices,getStorageDir,isAdminAuthEnabled,isMcpStreamRequest,packageVersion,registerRoutes,renderEditorSidebarJs,renderEditorSidebarLoaderHtml,requireAccessLink,resolveAiAccessSettings});
7092
+ </html>`}a(vc,"renderMcpPage");Oe();nt();Te();Ve();oe();Mt();se();rt();Wt();Oe();Fo();nt();Pe();function qe(e,t,n,r={}){e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("Content-Type","application/json; charset=utf-8");let o={error:ot[n]??"Request failed",code:n,...r},s=we(n,"rest");s&&typeof o.hint!="string"&&(o.hint=s),e.status(t).json(o)}a(qe,"sendSafeJsonError");function $r(e){let t={kind:"admin"};return typeof e.user?.username=="string"&&(t.username=e.user.username),t}a($r,"adminActor");function kc(e,t,n,r,o){return async(s,i)=>{if(!s.accessLink){i.setHeader("WWW-Authenticate",fe),i.status(401).json({error:Ie,code:at});return}try{let d=V(s,r),l=ge(s.query),c=await Tt(e,t,s.accessLink,s.body??{},n,d,o),u=te(e),p=Qe(e,e.get(c.id),s.accessLink.id),f=c.warnings;i.status(201).json({...ie(c,{...d,include:l}),mine:!0,...et(p),...pe(u),...f&&f.length>0?{warnings:f}:{}})}catch(d){let l=W(d,"rest");if(l){i.status(l.statusCode).json(l.body);return}B(i,d);return}}}a(kc,"createProposalHandler");function Ac(e,t,n){return async(r,o)=>{let s=await tt(t),i=s?.rev,d=s?ho(s.flows):void 0,l={...V(r,n),...d?{liveFlowLabels:d}:{}},c=ge(r.query),u;try{u=_o(r.query)}catch(v){B(o,v);return}let p=s?De(e,s).expired:te(e),f=r.accessLink;if(f){let v=En(e,f,l,u),L=v.proposals.map(P=>{let I=he(e,f,P,{...l,include:c},{includeConflicts:!1});return!U(I.status)||i===void 0?I:{...I,baseRevChanged:I.expectedRev!==i}});o.json({proposals:L,...Jt({...v,returned:v.proposals.length}),...pe(p)});return}let m={};u.flowId&&(m.flowId=u.flowId);let h=za(e.list(m).filter(v=>fi(v.status,u.status)),{...l,include:c}),g=u.offset??0,w=(u.limit!==void 0?h.slice(g,g+u.limit):g>0?h.slice(g):h).map(v=>!U(v.status)||i===void 0?v:{...v,baseRevChanged:v.expectedRev!==i});o.json({proposals:w,...Jt({total:h.length,limit:u.limit??null,offset:g,returned:w.length}),...pe(p)})}}a(Ac,"listProposalsHandler");function xc(e,t,n){return async(r,o)=>{let s=r.params.id;if(typeof s!="string"||s.length===0){o.status(400).json({error:"Missing proposal id"});return}let i=V(r,t),d=ge(r.query),l=await tt(n);try{if(r.accessLink){let c=ut(e,r.accessLink,s,i);o.json(he(e,r.accessLink,c,{...i,include:d},{...l?{live:l}:{}}));return}l?De(e,l):te(e),o.json(ie(e.get(s),{...i,include:d}))}catch(c){if(c instanceof me){qe(o,404,c.code);return}let u=W(c,"rest");if(u){o.status(u.statusCode).json(u.body);return}B(o,c);return}}}a(xc,"getProposalHandler");function Sc(e,t){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){r.status(400).json({error:"Missing proposal id"});return}let s=n.body??{},i={};if(s.mergedHash!==void 0){if(typeof s.mergedHash!="string"||!/^[0-9a-f]{8,64}$/.test(s.mergedHash)){r.status(400).json({error:"mergedHash must be a hex string",code:"invalid_body"});return}i.mergedHash=s.mergedHash}if(s.deploymentType!==void 0){if(s.deploymentType!=="nodes"&&s.deploymentType!=="flows"&&s.deploymentType!=="full"){r.status(400).json({error:"deploymentType must be 'nodes', 'flows', or 'full'"});return}i.deploymentType=s.deploymentType}try{let d=await e.approve(o,$r(n),i);r.json(ue(d,V(n,t)))}catch(d){if(d instanceof Je){qe(r,409,d.code,{conflicts:d.conflicts});return}if(d instanceof Yt){qe(r,409,d.code,{mergedHash:d.mergedHash,rebaseState:d.rebaseState});return}if(d instanceof Xt||d instanceof Pt||d instanceof Zt){qe(r,d.statusCode,d.code);return}if(d instanceof me||d instanceof Y||d instanceof Re||d instanceof ce){qe(r,d.statusCode,d.code);return}B(r,d);return}}}a(Sc,"approveProposalHandler");function Tc(e,t){return(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){r.status(400).json({error:"Missing proposal id"});return}let s=n.body??{},i=typeof s.reason=="string"?s.reason:"Rejected by admin";try{let d=ue(e.reject(o,$r(n),i),V(n,t));r.json(d)}catch(d){if(d instanceof me||d instanceof Y||d instanceof ce){qe(r,d.statusCode,d.code);return}B(r,d);return}}}a(Tc,"rejectProposalHandler");function Rc(e,t,n){return(r,o)=>{let s=r.params.id;if(typeof s!="string"||s.length===0){o.status(400).json({error:"Missing proposal id",code:"missing_proposal_id"});return}let i=r.body??{},d=V(r,n),l=ge(r.query);try{if(!r.accessLink){let u=gi(e,t,$r(r),s,i.reason,d);o.json(ie(u,{...d,include:l}));return}let c=In(e,t,r.accessLink,s,i.reason,d);o.json(he(e,r.accessLink,c,{...d,include:l}))}catch(c){let u=W(c,"rest");if(u){qe(o,u.statusCode,u.body.code);return}qe(o,500,"internal_error");return}}}a(Rc,"withdrawProposalHandler");function Pc(e,t){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){r.status(400).json({error:"Missing proposal id"});return}try{let s=n.accessLink?ut(e,n.accessLink,o):e.get(o),i=Mn(n.query.format);if(!i.ok){r.status(400).json({error:"Invalid diff format",code:"invalid_diff_format",hint:"Valid formats: summary, full (default summary)."});return}let d=Dn(await Bn(s,Xe(n,t),e.get(o)),i.format);r.json(d)}catch(s){if(s instanceof me){qe(r,404,s.code);return}if(s instanceof X||s instanceof Le||s instanceof We){qe(r,s.statusCode,s.code);return}let i=W(s,"rest");if(i){r.status(i.statusCode).json(i.body);return}B(r,s);return}}}a(Pc,"getProposalDiffHandler");function _c(e,t,n,r,o){return async(s,i)=>{if(!s.accessLink){qe(i,401,"access_link_missing");return}try{let d=V(s,r),l=await Co(e,t,s.accessLink,String(s.params.id),s.body??{},n,d,o);i.json(he(e,s.accessLink,l,d))}catch(d){B(i,d)}}}a(_c,"resolveProposalHandler");function Cc(e,t,n,r){return async(o,s)=>{try{let i=o.body??{};if(i.choice==="undo"){let l=e.get(String(o.params.id)),c=l.conflictChoice;if(!c?.undo||c.nodeId!==i.nodeId){s.status(409).json({code:"conflict_not_found",error:"This choice can no longer be undone. Reload the proposal."});return}let u=e.updateBody(l.id,c.undo),p=await n.getFlows(),f=Me(e,u,p).proposal;t.append("proposal.conflict_resolved",$r(o),{proposalId:l.id,nodeId:c.nodeId,choice:"undo"}),s.json(ie(f,V(o,r)));return}let d=await yi(e,t,$r(o),String(o.params.id),i.nodeId,i.choice,n);s.json(ie(d,V(o,r)))}catch(i){B(s,i)}}}a(Cc,"resolveProposalConflictHandler");function Ec(e,t,n){return async(r,o)=>{try{te(e);let s=e.get(String(r.params.id)),i=await tt(t);i&&U(s.status)&&(s=Me(e,s,i).proposal),o.json(ie(s,V(r,n)))}catch(s){B(o,s)}}}a(Ec,"recheckProposalHandler");Pe();Te();Te();ao();function fy(e){return e.endsWith("/")?e.slice(0,-1):e}a(fy,"trimTrailingSlash");var cs=class{static{a(this,"HttpAdapter")}kind="http";baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=fy(t.baseUrl),this.accessToken=t.accessToken,this.fetchImpl=t.fetch??globalThis.fetch.bind(globalThis)}headers(t){let n=new Headers(t);return n.set("Accept","application/json"),n.set("Node-RED-API-Version","v2"),this.accessToken&&n.set("Authorization",`Bearer ${this.accessToken}`),n}async request(t,n={}){return await this.fetchImpl(`${this.baseUrl}${t}`,{...n,headers:this.headers(n.headers)})}async readJson(t,n){let r=await t.text();if(!t.ok){let o=r;try{let s=JSON.parse(r);o=s.message??s.code??r}catch{}throw t.status===404?new X(n):t.status===409?new Q("unknown","unknown",`Admin API ${n}`):t.status===401||t.status===403?new We(eo):new Error(`Admin API ${n} failed (${t.status}): ${o}`)}if(!r)return null;try{return JSON.parse(r)}catch{throw new Error(`Admin API ${n} returned non-JSON`)}}async getFlows(){let t=await this.request("/flows"),n=await this.readJson(t,"GET /flows");return dr(n,"GET /flows")}async getFlow(t){let n=encodeURIComponent(t),[r,o]=await Promise.all([this.request("/flows"),this.request(`/flow/${n}`)]);if(o.status===404)throw new X(t);let s=await this.readJson(r,"GET /flows"),i=await this.readJson(o,`GET /flow/${t}`);return{rev:dr(s,"GET /flows").rev,flow:vn(t,i)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new Q(t.expectedRev,r.rev,"updateFlow");let o=io(r.flows,t.flowId,t.flow),s=await this.request("/flows",{method:"POST",headers:{"Content-Type":"application/json","Node-RED-Deployment-Type":n},body:JSON.stringify({flows:o,rev:t.expectedRev})});if(s.status===409){let d=r.rev;try{let l=await s.clone().json();typeof l.current_rev=="string"&&(d=l.current_rev)}catch{}throw new Q(t.expectedRev,d,"updateFlow")}let i=await this.readJson(s,"POST /flows");if(!i||typeof i!="object"||typeof i.rev!="string")throw new Error("Admin API POST /flows did not return a rev");return{flowId:t.flowId,rev:i.rev}}};Te();ao();function hy(e){let t=e.runtime?.flows;if(!t?.getFlows||!t.getFlow||!t.setFlows)throw new Error("RED.runtime.flows is missing getFlows, getFlow, or setFlows");return t}a(hy,"requireFlowsApi");function my(e){if(typeof e=="string")return e;if(typeof e.rev=="string"&&e.rev.length>0)return e.rev;throw new Error("RED.runtime.flows.setFlows did not return a rev")}a(my,"resolveRev");var us=class{static{a(this,"RedAdapter")}kind="red";flows;constructor(t){this.flows=hy(t)}async getFlows(){let t=await this.flows.getFlows({});return dr(t,"RED.runtime.flows.getFlows()")}async getFlow(t){let n=await this.getFlows(),r;try{r=await this.flows.getFlow({id:t})}catch(o){throw gy(o)?new X(t):o}return{rev:n.rev,flow:vn(t,r)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new Q(t.expectedRev,r.rev,"updateFlow");let o=io(r.flows,t.flowId,t.flow),s;try{s=await this.flows.setFlows({flows:{flows:o,rev:t.expectedRev},deploymentType:n})}catch(i){if(i&&typeof i=="object"&&"code"in i&&i.code==="version_mismatch"){let d=await this.getFlows();throw new Q(t.expectedRev,d.rev,"updateFlow")}throw i}return{flowId:t.flowId,rev:my(s)}}};function Ic(e){let t=e?.runtime?.flows;return!!(t?.getFlows&&t.getFlow&&t.setFlows)}a(Ic,"canUseRedRuntime");function gy(e){if(!e||typeof e!="object")return!1;let t=e;if((typeof t.status=="number"?t.status:t.statusCode)===404||t.code==="not_found"||t.code==="flow_not_found")return!0;let r=typeof t.message=="string"?t.message.toLowerCase():"";return r.includes("not found")||r.includes("unknown flow")}a(gy,"isRuntimeFlowNotFound");var Lc=require("module");function Nc(e){if(!e||typeof e!="object")return!1;let t=e;return typeof t.getFlows=="function"&&typeof t.getFlow=="function"&&typeof t.setFlows=="function"}a(Nc,"isFlowsApi");function yy(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Lc.createRequire)(e)}a(yy,"createPluginRequire");function wy(e){try{let t=e("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}a(wy,"isNodeRedRuntimeInitialized");function by(){try{let e=yy();if(!wy(e))return null;let t=e("@node-red/runtime");return Nc(t.flows)?t.flows:null}catch{return null}}a(by,"tryRequireInitializedRuntimeFlows");function Fc(e){if(e?.runtime&&Nc(e.runtime.flows))return e;let t=by();return t?{runtime:{flows:t}}:null}a(Fc,"resolveRuntimeHost");function Oc(e={}){let t=Fc(e.RED??null);if(t&&Ic(t))return new us(t);let n=e.adminAuthEnabled===!0;if(e.http?.baseUrl){if(n&&!e.http.accessToken)throw new Le("In-process Node-RED flow runtime is unavailable and HTTP Admin API fallback has no credentials while adminAuth is enabled. Ensure the plugin runs inside Node-RED so @node-red/runtime.flows can be used.");return new cs(e.http)}throw new Le("No usable Node-RED flow adapter: in-process flows API is unavailable and no HTTP Admin API baseUrl was provided")}a(Oc,"createFlowAdapter");Te();Es();var fs=require("fs"),$c=require("path");no();oe();var vy=/^[a-z][a-z0-9._-]{0,63}$/;function ky(e){if(e.kind==="system")return{kind:"system"};if(e.kind==="access-link"){let n={kind:"access-link",linkId:String(e.linkId).replace(/\0/g,"").slice(0,200)};return typeof e.label=="string"&&(n.label=e.label.replace(/\0/g,"").slice(0,200)),n}let t={kind:"admin"};return typeof e.username=="string"&&(t.username=e.username.replace(/\0/g,"").slice(0,200)),t}a(ky,"sanitizeActor");var ps=class{constructor(t,n={}){this.paths=t;this.filePath=(0,$c.join)(t.logs,"audit.jsonl"),this.now=n.now??(()=>Date.now()),this.maxAppendBytes=n.maxAppendBytes??ta}static{a(this,"AuditLog")}filePath;now;maxAppendBytes;seq=0;append(t,n,r){let o=vy.test(t)?t:"unknown";this.seq+=1;let s={id:`log-${this.now()}-${this.seq}`,at:new Date(this.now()).toISOString(),action:o,actor:ky(n)};r!==void 0&&(s.details=va(r));let i=ht(s,this.maxAppendBytes,{kind:"audit"});return ra(this.paths.root,this.filePath,i),s}list(t=100){if(!(0,fs.existsSync)(this.filePath))return[];let n=(0,fs.readFileSync)(this.filePath,"utf8").split(`
7093
+ `).map(o=>o.trim()).filter(Boolean),r=[];for(let o of n)try{r.push(JSON.parse(o))}catch{}return r.slice(-Math.max(1,t)).reverse()}};Ve();Kr();ve();Ut();Fo();ke();function Ay(e){let t=a(()=>Promise.reject(new We(e)),"fail");return{kind:"http",getFlows:t,getFlow:t,updateFlow:t}}a(Ay,"misconfiguredAdapter");function Mc(e){let t=e.settings,n=typeof t.uiPort=="number"?t.uiPort:1880,r=t.httpAdminRoot??"/",o=!r||r==="/"?"":r.replace(/\/$/,"");return`http://127.0.0.1:${n}${o}`}a(Mc,"localAdminBaseUrl");function hs(e,t={}){let n=Ur(e.settings.userDir);oa(n);let r=t.settingsStore??new hn(n,e.settings.aiAccess,u=>e.log?.warn?.(u));t.bindSettings!==!1&&Vr(r),Xr(r.getEffective().publicBaseUrl);let o=t.linkStore??is(e);o.configureDefaults({defaultTtlSeconds:r.getEffective().defaultLinkTtlSeconds,maxActiveLinks:r.getEffective().maxActiveLinks});let s=new ro(n),i=new ps(n),d;if(t.flowAdapter)d=t.flowAdapter;else try{d=Oc({RED:e,http:{baseUrl:Mc(e)},adminAuthEnabled:re(e.settings)})}catch(u){let p=u instanceof Le?u.message:eo;e.log?.warn?.(`AI Access flow adapter unavailable: ${p}`),d=Ay(p)}d=Sa(d);let l;if(t.nodeTypesAdapter)l=t.nodeTypesAdapter;else if(t.flowAdapter)l=$s();else try{l=Na({baseUrl:Mc(e)})}catch(u){let p=u instanceof Error?u.message:String(u);e.log?.warn?.(`AI Access node-types adapter unavailable: ${p}`),l=$s([])}let c=new No(s,i,d,()=>Ce(e.settings).defaultDeploymentType,e.settings.aiAccess);return{linkStore:o,proposalStore:s,auditLog:i,flowAdapter:d,nodeTypesAdapter:l,proposalService:c,settingsStore:r,...t.catalogueFetcher?{catalogueFetcher:t.catalogueFetcher}:{},...t.catalogueUrl?{catalogueUrl:t.catalogueUrl}:{}}}a(hs,"createPluginServices");oe();ve();ze();Br();var Dc=Vn(require("dns"),1),Hc=Vn(require("http"),1),jc=Vn(require("https"),1),Mr=require("net");ve();function xy(e,t){return e===10||e===172&&t>=16&&t<=31||e===192&&t===168}a(xy,"isPrivateV4");function Sy(e,t){return e===169&&t===254}a(Sy,"isLinkLocalV4");function Ty(e,t,n){return e===0||e===100&&t>=64&&t<=127||e>=224||e===192&&t===0&&(n===0||n===2)||e===198&&(t===18||t===19||t===51&&n===100)||e===203&&t===0&&n===113}a(Ty,"isSpecialUseV4");function Ry(e){let[t=0,n=0,r=0]=e.split(".").map(Number);return t===127?"loopback":xy(t,n)||Sy(t,n)||Ty(t,n,r)||e==="168.63.129.16"?"blocked":"public"}a(Ry,"classifyIpv4");function Py(e){let t=e.split(":"),n=t.indexOf(""),r=[];for(let o of t){if(!o)continue;if(o.includes(".")){let i=o.split(".").map(Number);if(i.length!==4||i.some(d=>!Number.isInteger(d)||d<0||d>255))return;r.push((i[0]??0)<<8|(i[1]??0)),r.push((i[2]??0)<<8|(i[3]??0));continue}let s=Number.parseInt(o,16);if(!/^[\da-f]{1,4}$/i.test(o)||!Number.isInteger(s))return;r.push(s)}if(n>=0){let o=8-r.length;if(o<1)return;let s=t.slice(0,n).filter(Boolean).length;r.splice(s,0,...Array(o).fill(0))}else if(r.length!==8)return;return r.length===8?r:void 0}a(Py,"expandIpv6");function _y(e){let t=Py(e);if(!t)return"blocked";if(t.slice(0,7).every(s=>s===0)&&t[7]===1)return"loopback";let n=t[0]??0,r=t[1]??0;return n===0||n<8192||n>16383||n===8193&&r<=511||n===8193&&r===3512||n===16383&&r<=4095||n===8194?"blocked":"public"}a(_y,"classifyIpv6");function Dr(e){let t=Dt(e);return t?(0,Mr.isIP)(t)===4?Ry(t):_y(t):"blocked"}a(Dr,"classifyPublicUrlAddress");function Cy(e){let t=e.toLowerCase().replace(/\.$/,"");return["metadata","metadata.google.internal","metadata.google.com","metadata.goog","instance-data","instance-data.ec2.internal","metadata.azure.internal","metadata.aws.internal"].some(n=>t===n||t.endsWith(`.${n}`))}a(Cy,"isMetadataHostname");async function Uc(e,t,n){let r=mn(e);if(!r)throw new Error("publicBaseUrl is required for Check");let o=new URL(r),s=o.hostname.replace(/^\[|\]$/g,"");if(Cy(s))throw new Error("Public URL Check cannot target metadata hosts");let i=Dt(s),l=(typeof e=="string"?/^https?:\/\/(\[[^\]]+\]|[^/?#:]+)/i.exec(e.trim())?.[1]:void 0)?.replace(/^\[|\]$/g,"");if(i&&l&&l.toLowerCase()!==i.toLowerCase())throw new Error("Public URL Check requires canonical IP address notation");let c=s.toLowerCase().replace(/\.$/,"")==="localhost"||i!==void 0&&Dr(i)==="loopback",u=Number(o.port||(o.protocol==="https:"?443:80));if(c&&u!==t)throw new Error("Public URL Check may only target this Node-RED instance's port for loopback URLs");let p=a(g=>{let y=Dr(g);if(y==="blocked"||y==="loopback"&&!c||c&&y!=="loopback")throw new Error("Public URL Check cannot target private or non-public addresses")},"validateAddress");if(i)return p(i),{origin:r,address:i,family:(0,Mr.isIP)(i)};let f=await new Promise((g,y)=>{let w=a(()=>y(n.reason instanceof Error?n.reason:new Error(String(n.reason))),"abort");n.throwIfAborted(),n.addEventListener("abort",w,{once:!0}),Dc.default.lookup(s,{all:!0},(v,L)=>{n.removeEventListener("abort",w),v?y(v):g(L)})});if(n.throwIfAborted(),!f.length)throw new Error("Public URL Check hostname has no addresses");for(let g of f)p(g.address);let m=[];for(let g of f){let y=Dt(g.address);if(!y)throw new Error("Public URL Check returned an invalid address");m.some(w=>w.address===y)||m.push({address:y,family:(0,Mr.isIP)(y)})}c&&m.sort((g,y)=>g.family-y.family);let h=m[0];if(!h)throw new Error("Public URL Check hostname has no addresses");return{origin:r,...h,addresses:m}}a(Uc,"preparePublicUrlCheck");function Ey(e,t,n){let r=e.hostname.replace(/^\[|\]$/g,"");return new Promise((o,s)=>{let d=(e.protocol==="https:"?jc.default:Hc.default).request(e,{method:"GET",agent:!1,signal:n,headers:{Host:e.host},...(0,Mr.isIP)(r)?{}:{servername:r},lookup:a((l,c,u)=>{c.all?u(null,[{address:t.address,family:t.family}]):u(null,t.address,t.family)},"lookup")},l=>{o(l.statusCode??0),l.destroy()});d.on("error",s),d.end()})}a(Ey,"requestPinned");async function Bc(e,t,n){let r=new URL(e.origin+t),o=e.addresses?.length?e.addresses:[{address:e.address,family:e.family}],s;for(let i of o){n.throwIfAborted();try{return await Ey(r,i,n)}catch(d){if(n.aborted)throw d;s=d}}throw s instanceof Error?s:new Error("Public URL Check request failed")}a(Bc,"requestPublicUrlCheck");pn();ke();function Iy(e){return rr(e.settings.httpAdminRoot)}a(Iy,"mcpPageUrl");function Ly(e){return typeof e=="boolean"||typeof e=="number"||typeof e=="string"?e:Array.isArray(e)?e.join(", "):typeof e=="function"?"custom function":!1}a(Ly,"normalizeTrustProxy");function Wc(e){if(!e)return"Set a Public URL before enabling OAuth.";let t=new URL(e),n=Dt(t.hostname),r=t.hostname.toLowerCase().replace(/\.$/,"")==="localhost"||n==="::1"||n?.startsWith("127.");return t.protocol==="http:"&&!r?"OAuth Public URL uses HTTP; HTTPS is recommended.":null}a(Wc,"oauthPublicUrlWarning");function Gc(e,t,n){let{sources:r,...o}=t.getEffective(),{permission:s}=Yr(e,n);return{ok:!0,adminAuthEnabled:re(e.settings),canEdit:s==="*",permission:s,mcpPageUrl:Iy(e),apiBase:xe(e.settings.httpAdminRoot),settings:o,oauthPublicUrlWarning:Wc(o.publicBaseUrl),sources:r,disk:t.getDisk(),trustProxy:Ly(n.app.get("trust proxy")),precedence:["settings.js","disk","default"],defaults:{secretNamePatterns:[...Ot]}}}a(Gc,"settingsResponse");function Kc(e,t){return(n,r)=>{r.json(Gc(e,t,n))}}a(Kc,"getSettingsHandler");function qc(e,t,n){let r=e[t];if(r!==void 0){if(typeof r!="boolean")throw new Error(`${t} must be a boolean`);n[t]=r}}a(qc,"parseBooleanField");function zc(e,t,n){let r=e[t];if(r!==void 0){if(typeof r!="number"||!Number.isInteger(r)||r<1)throw new Error(`${t} must be an integer with a minimum of 1`);n[t]=Math.floor(r)}}a(zc,"parsePositiveIntField");function Ny(e,t,n){let r=e[t];if(r===void 0)return;let o=t==="rateLimitWindowMs"?100:1,s=t==="rateLimitWindowMs"?36e5:1e6;if(typeof r!="number"||!Number.isInteger(r)||r<o||r>s)throw new Error(`${t} must be an integer between ${o} and ${s}`);n[t]=r}a(Ny,"parseRateLimitField");function Fy(e,t){if(e.corsAllowedOrigins===void 0)return;if(!Array.isArray(e.corsAllowedOrigins))throw new Error("corsAllowedOrigins must be a list of HTTP(S) origins");let n=e.corsAllowedOrigins.map(r=>Zn(r));if(n.some(r=>r===void 0))throw new Error("corsAllowedOrigins must contain only HTTP(S) origins without paths, credentials, queries, or fragments");t.corsAllowedOrigins=[...new Set(n)]}a(Fy,"parseCorsField");function Oy(e,t,n){let r=e.secretNamePatterns;if(r!==void 0){if(r===null){n.push("secretNamePatterns");return}if(!Array.isArray(r)||r.some(o=>typeof o!="string"))throw new Error("secretNamePatterns must be a list of strings (or null to reset)");if(r.length>Qn)throw new Error(`secretNamePatterns allows at most ${Qn} entries`);for(let o of r)if(!qr.test(o.trim()))throw new Error('secretNamePatterns entries must be 2-40 letters, digits, "_" or "-" (e.g. API_KEY)');t.secretNamePatterns=er(r)??[]}}a(Oy,"parseSecretPatternsField");function Jc(e,t,n){return(r,o)=>{let s=r.body;if(!s||typeof s!="object"||Array.isArray(s)){o.status(400).json({error:"Request body must be a settings object",code:"invalid_body"});return}let i={...s};for(let[c,u]of Object.entries(t.getEffective().sources))u==="settings.js"&&delete i[c];t.getEffective().sources.oauth==="settings.js"&&delete i.oauth;let d={},l=[];try{if(i.proposalTtlDays!==void 0){let c=i.proposalTtlDays;if(typeof c!="number"||!Number.isInteger(c)||c<1||c>365)throw new Error("proposalTtlDays must be an integer between 1 and 365");d.proposalTtlDays=c}if(i.oauth!==void 0){if(!i.oauth||typeof i.oauth!="object"||Array.isArray(i.oauth))throw new Error("oauth must be an object");let c=i.oauth,u={};if(c.enabled!==void 0){if(typeof c.enabled!="boolean")throw new Error("oauth.enabled must be a boolean");u.enabled=c.enabled}for(let f of["accessTokenTtlSec","refreshIdleTtlSec","dcrMaxClients"])if(c[f]!==void 0){if(typeof c[f]!="number"||!Number.isInteger(c[f])||c[f]<1||c[f]>31536e3)throw new Error(`oauth.${f} must be an integer with a minimum of 1`);u[f]=c[f]}let p=c.refreshMaxLifetimeSec;if(p!==void 0){if(typeof p!="number"||!Number.isInteger(p)||p<0||p>31536e4)throw new Error("oauth.refreshMaxLifetimeSec must be a non-negative integer");u.refreshMaxLifetimeSec=p}d.oauth=u}qc(i,"enabled",d),qc(i,"allowWithoutAdminAuth",d),zc(i,"defaultLinkTtlSeconds",d),zc(i,"maxActiveLinks",d)}catch(c){o.status(400).json({error:c.message,code:"invalid_body"});return}if(i.defaultDeploymentType!==void 0){let c=i.defaultDeploymentType;if(c!=="nodes"&&c!=="flows"&&c!=="full"){o.status(400).json({error:"defaultDeploymentType must be 'nodes', 'flows', or 'full'",code:"invalid_body"});return}d.defaultDeploymentType=c}try{i.publicBaseUrl!==void 0&&(d.publicBaseUrl=mn(i.publicBaseUrl)),Fy(i,d),Oy(i,d,l);for(let c of["rateLimitWindowMs","rateLimitMaxPerIp","rateLimitMaxPerToken"])Ny(i,c,d)}catch(c){o.status(400).json({error:c.message,code:"invalid_body"});return}try{let c=t.patch(d,l);Xr(c.publicBaseUrl),n.configureDefaults({defaultTtlSeconds:c.defaultLinkTtlSeconds,maxActiveLinks:c.maxActiveLinks});let u=Gc(e,t,r),p=c.publicBaseUrl?new URL(c.publicBaseUrl):void 0,f=p?Dt(p.hostname.replace(/^\[|\]$/g,"")):void 0,m=f?Dr(f):void 0,h=[];m!==void 0&&m!=="public"&&h.push("Public URL uses a private or non-public IP address.");let g=Wc(c.publicBaseUrl);g&&c.publicBaseUrl&&h.push(g),o.json({...u,...h.length?{warnings:h}:{}})}catch(c){if(c instanceof ce){o.status(c.statusCode).json({error:c.message,code:c.code});return}B(o,c);return}}}a(Jc,"putSettingsHandler");function Vc(e){return async(t,n)=>{let r=new AbortController,o=setTimeout(()=>r.abort(),5e3),s;try{let i;try{let c=e.settings,p=e.server?.address(),f=t.body?.publicBaseUrl,m=typeof f=="string"?new URL(f.trim()).hostname:"";if((m.toLowerCase().replace(/^\[|\]$/g,"").replace(/\.$/,"")==="localhost"||Dr(m.replace(/^\[|\]$/g,""))==="loopback")&&typeof p=="string")throw new Error("Public URL Check does not support loopback URLs when Node-RED listens on a Unix socket");let g=p&&typeof p=="object"?p.port:c.uiPort??1880;i=await Uc(t.body?.publicBaseUrl,g,r.signal)}catch(c){if(r.signal.aborted)throw c;n.status(400).json({error:c.message,code:"invalid_body"});return}s=i.origin+xe(e.settings.httpAdminRoot);let d=await Bc(i,xe(e.settings.httpAdminRoot),r.signal),l={status:d,ok:d>=200&&d<300};n.json({ok:l.ok,status:l.status,url:s,message:l.ok?"Discovery endpoint is reachable":`Discovery endpoint returned HTTP ${l.status}`})}catch{n.json({ok:!1,status:null,url:s,error:r.signal.aborted?"Public URL check timed out after 5 seconds":"Could not reach the discovery endpoint"})}finally{clearTimeout(o)}}}a(Vc,"checkPublicUrlHandler");ke();Mt();ke();se();Kr();var $y="com55-ai-access",ms=new WeakMap;function Xc(e){let t=new Set;for(let n of String(e??"").split(",")){let[r,...o]=n.split(";").map(d=>d.trim().toLowerCase());if(!r)continue;let s=o.find(d=>d.startsWith("q=")),i=s?Number(s.slice(2)):1;Number.isFinite(i)&&i>0&&t.add(r)}return t}a(Xc,"acceptedMediaTypes");function Zc(e){let t=Xc(e.headers.accept);return t.has("text/event-stream")&&!t.has("text/html")}a(Zc,"isMcpStreamRequest");function My(e,t,n){e.httpAdmin.get("/mcp/api/v1",t,n,Ai(e)),e.httpAdmin.get("/mcp/api/v1/",t,n,Ai(e)),e.httpAdmin.get("/mcp/api/v1/openapi.yaml",t,n,mc()),e.httpAdmin.get("/mcp/api/v1/openapi.json",t,n,gc()),e.httpAdmin.get("/mcp/editor/ai-access-sidebar.js",t,(r,o)=>{o.setHeader("Cache-Control","no-store, no-cache, must-revalidate, max-age=0"),o.setHeader("Pragma","no-cache"),o.setHeader("Expires","0"),o.setHeader("X-AI-Access-Sidebar-Version",$n),o.type("application/javascript").status(200).send(Oo())})}a(My,"registerMcpShellAndDiscovery");var Dy=/^\/mcp\/(?:api\/v1\/proposals|rpc$|a\/[^/]+\/(?:proposals|propose|draft))/;function Qc(e,t={}){let n=hs(e,{...t,linkStore:t.linkStore??is(e)});xd(()=>n.settingsStore.getEffective().proposalTtlDays),ms.get(e)?.();let r=Td(()=>vr(n.proposalStore,n.flowAdapter),500),o=a(()=>{r.cancel(),e.events?.removeListener?.("flows:started",r.trigger)},"cleanup");ms.set(e,o),e.events?.on("flows:started",r.trigger);let s=ol(()=>n.settingsStore.getEffective()),{ip:i,token:d}=s,l=new ds(Ur(e.settings.userDir),()=>n.settingsStore.getEffective().oauth.dcrMaxClients),c=a(()=>n.settingsStore.getEffective().publicBaseUrl,"publicBase"),u=a(()=>`${c().replace(/\/$/,"")}/mcp`,"mcpUrl"),p=new ls(l,u,()=>n.settingsStore.getEffective().oauth.accessTokenTtlSec,()=>n.settingsStore.getEffective().oauth.refreshIdleTtlSec,()=>n.settingsStore.getEffective().oauth.refreshMaxLifetimeSec),f=uc(e,p,c,u);ia(e.httpAdmin,sa(()=>n.settingsStore.getEffective().corsAllowedOrigins));let m=a(A=>(Array.isArray(A)?A:A?[A]:[]).flatMap(S=>{if(typeof S=="string")return[S];let N=S;return N&&typeof N.path=="string"?[{path:N.path,root:typeof N.root=="string"?N.root:void 0}]:[]}),"staticMounts"),h={authorization:[],metadata:[],details:[],httpNodeRootFallback:e.settings.httpNodeRoot===!1||!e.httpNode},g="",y=a(()=>{let A=cc({httpAdmin:e.httpAdmin,httpNode:e.httpNode,httpAdminRoot:e.settings.httpAdminRoot,httpNodeRoot:e.settings.httpNodeRoot,metadataFallback:h.httpNodeRootFallback,eachNode:e.nodes?M=>e.nodes?.eachNode(M):void 0,staticDirs:m(e.settings.httpStatic),httpStaticRoot:e.settings.httpStaticRoot,fileExists:Yc.existsSync}),S=A.authorization,N=A.metadata;h.authorization=S.map(Ki),h.metadata=N.map(Ki),h.details=[...S.map(M=>({...M,kind:"authorization"})),...N.map(M=>({...M,kind:"metadata"}))];let b=[...h.authorization.map(M=>`${M} \u2014 OAuth authorization routes disabled`),...h.metadata.map(M=>`${M} \u2014 OAuth metadata disabled`)],O=b.join(`
7094
+ `);if(O!==g)for(let M of b)e.log?.warn?.(`AI Access OAuth route collision: ${M}`);g=O},"refreshCollisions");y(),e.events?.on("flows:started",y);let w=Ri({windowMs:6e4,maxPerIp:30}),v=a((A,S,N)=>{if(!n.settingsStore.getEffective().oauth.enabled||h.authorization.length||!A.path.startsWith("/mcp/oauth/")){N();return}let b=A.path.replace(/\/+$/,"").toLowerCase(),O=a(()=>gn(e)(A,S,()=>f.server(A,S,N)),"run");b==="/mcp/oauth/authorize"?i(A,S,()=>w(A,S,O)):b.startsWith("/mcp/oauth/consent/")?i(A,S,O):O()},"oauthServerMiddleware");v.oauthOwned=!0,e.httpAdmin.use(v);let L=a((A,S,N)=>{if(!n.settingsStore.getEffective().oauth.enabled||h.metadata.length){N();return}f.metadata(A,S,N)},"metadataHandler");L.oauthOwned=!0,h.httpNodeRootFallback?e.httpAdmin.use("/mcp",L):e.httpNode?.use(L),e.httpAdmin.get("/mcp/api/v1/oauth/clients",i,d,Se(e,"api"),(A,S)=>S.json({clients:l.data.clients.map(N=>({client_id:N.client_id,client_name:N.client_name,redirect_uris:N.redirect_uris,lastUsedAt:N.lastUsedAt}))})),e.httpAdmin.get("/mcp/api/v1/oauth/status",i,d,Se(e,"api"),(A,S)=>{let N=!re(e.settings)||gt(A)==="*";S.json({...h,details:N?h.details:h.details.map(({file:b,...O})=>O),collisions:[...h.authorization,...h.metadata],oauthEnabled:n.settingsStore.getEffective().oauth.enabled})}),e.httpAdmin.delete("/mcp/api/v1/oauth/clients/:id",i,d,Se(e,"api"),yn(e),(A,S)=>S.json({revoked:l.revokeClient(String(A.params.id))})),e.httpAdmin.use((A,S,N)=>{if(!Dy.test(A.path)){N();return}n.flowAdapter.getFlows().then(b=>Ba(A,ho(b.flows))).catch(()=>{}).finally(()=>N())}),e.httpAdmin.use(Kl(n.auditLog)),_l(e,{linkStore:n.linkStore,proposalStore:n.proposalStore,auditLog:n.auditLog,flowAdapter:At(n.flowAdapter),nodeTypesAdapter:n.nodeTypesAdapter,...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{},rateLimiters:s}),e.httpAdmin.get("/ai-access",i,d,(A,S)=>{S.append("Content-Security-Policy","form-action 'none'; frame-ancestors 'none'"),S.setHeader("X-Frame-Options","DENY"),S.setHeader("Cache-Control","no-store, no-cache, must-revalidate, max-age=0"),S.setHeader("Pragma","no-cache"),S.setHeader("Expires","0"),S.type("html").status(200).send(vc({apiBase:xe(e.settings.httpAdminRoot),mcpRpc:mt(e.settings.httpAdminRoot),editorRoot:Ae(e.settings.httpAdminRoot),authTokenKey:Qr(e.settings.httpAdminRoot),dashboardPath:rr(e.settings.httpAdminRoot)}))}),e.httpAdmin.get("/mcp",i,(A,S,N)=>{if(Zc(A)){N();return}S.setHeader("Cache-Control","no-store"),S.redirect(302,rr(e.settings.httpAdminRoot))}),My(e,i,d),e.httpAdmin.get("/mcp/api/v1/status",i,d,pa(e,A=>n.linkStore.validateResult(A)),$((A,S)=>{S.json(qn(e,A))})),e.httpAdmin.get("/mcp/api/v1/settings",i,d,Se(e,"api"),$(Kc(e,n.settingsStore))),e.httpAdmin.put("/mcp/api/v1/settings",i,d,Se(e,"api"),yn(e),je("rest"),$(Jc(e,n.settingsStore,n.linkStore))),e.httpAdmin.post("/mcp/api/v1/settings/check-public-url",i,d,Se(e,"api"),yn(e),je("rest"),$(Vc(e)));let P=[i,d,Se(e,"api"),gn(e),yn(e)];e.httpAdmin.get("/mcp/api/v1/links",...P,$(Ml(e,n.linkStore))),e.httpAdmin.post("/mcp/api/v1/links",...P,$(Dl(e,n.linkStore,n.auditLog))),e.httpAdmin.delete("/mcp/api/v1/links/:id",...P,$(Hl(e,n.linkStore,n.auditLog)));let I=[i,d,gn(e),ua(e,A=>n.linkStore.validateResult(A))],D=n.flowAdapter,T=n.nodeTypesAdapter,x={nodeTypes:T,...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{}};e.httpAdmin.get("/mcp/api/v1/flows",...I,$(Jd(D))),e.httpAdmin.get("/mcp/api/v1/flows/:id",...I,$(Ko(D))),e.httpAdmin.get("/mcp/api/v1/nodes/:id",...I,$(Jo(D))),e.httpAdmin.get("/mcp/api/v1/nodes/:id/field/:name",...I,$(Vo(D))),e.httpAdmin.get("/mcp/api/v1/search",...I,$(Yo(D))),e.httpAdmin.get("/mcp/api/v1/node-types",...I,$(Xo(x))),e.httpAdmin.get("/mcp/api/v1/node-types/:type",...I,$(Zo(x))),e.httpAdmin.get("/mcp/api/v1/palette/search",...I,$(Qo(x)));let C=e.settings.httpAdminRoot;e.httpAdmin.get("/mcp/api/v1/proposals",...I,$(Ac(n.proposalStore,n.flowAdapter,C))),e.httpAdmin.get("/mcp/api/v1/proposals/:id",...I,$(xc(n.proposalStore,C,n.flowAdapter))),e.httpAdmin.get("/mcp/api/v1/proposals/:id/diff",...I,$(Pc(n.proposalStore,n.flowAdapter))),e.httpAdmin.post("/mcp/api/v1/proposals",i,d,je("rest"),bn(e,n.linkStore,"rest"),$(kc(n.proposalStore,n.auditLog,n.flowAdapter,C,n.nodeTypesAdapter))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/approve",...P,je("rest"),$(Sc(n.proposalService,C))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/reject",...P,je("rest"),$(Tc(n.proposalService,C))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/withdraw",...I,(A,S,N)=>{A.accessLink?N():Se(e,"api")(A,S,()=>yn(e)(A,S,N))},je("rest"),$(Rc(n.proposalStore,n.auditLog,C))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/resolve-conflict",...P,je("rest"),$(Cc(n.proposalStore,n.auditLog,D,C))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/recheck",...P,$(Ec(n.proposalStore,D,C))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/resolve",i,d,je("rest"),bn(e,n.linkStore,"rest"),$(_c(n.proposalStore,n.auditLog,D,C,T)));let E=[i,d,(A,S,N)=>{let b=A.headers.authorization;if(!n.settingsStore.getEffective().oauth.enabled||b?.toLowerCase().startsWith("bearer aia_")){if(n.settingsStore.getEffective().oauth.enabled){let O=S.setHeader.bind(S);S.setHeader=((M,F)=>{if(M.toLowerCase()==="www-authenticate"&&typeof F=="string"&&!F.includes("resource_metadata=")){let le=`${c().replace(/\/$/,"")}${h.httpNodeRootFallback?"/mcp":""}/.well-known/oauth-protected-resource/mcp`;F+=`, resource_metadata="${le}", scope="read propose"`}return O(M,F)})}bn(e,n.linkStore,"mcp")(A,S,N);return}gn(e)(A,S,()=>f.auth(A,S,N))}];e.httpAdmin.post("/mcp/rpc",...E,je("mcp"),$(ji(e,n))),e.httpAdmin.get("/mcp/rpc",...E,Nr),e.httpAdmin.delete("/mcp/rpc",...E,Nr),e.httpAdmin.post("/mcp",...E,je("mcp"),$(ji(e,n))),e.httpAdmin.get("/mcp",...E,Nr),e.httpAdmin.delete("/mcp",...E,Nr),e.httpAdmin.get("/mcp/api/v1/logs",i,d,Se(e,"api"),gn(e),$(jl(n.auditLog,n.linkStore))),e.httpAdmin.use("/mcp/api/v1",Wd()),Ll(e)}a(Qc,"registerRoutes");function Hy(e){e.plugins.registerPlugin($y,{type:"com55-ai-access",onremove(){ms.get(e)?.(),ms.delete(e)},onadd(){Qc(e),e.log?.info?.("Node-RED AI Access plugin loaded (dashboard /ai-access, MCP /mcp)")}})}a(Hy,"registerAiAccessPlugin");var jy=Hy;0&&(module.exports={ACCESS_LINK_INVALID_MESSAGE,ACCESS_LINK_MISSING_MESSAGE,AccessLinkStore,EDITOR_SIDEBAR_ASSET_VERSION,MAX_PROPOSAL_JSON_BYTES,SettingsStore,acceptedMediaTypes,areAiFeaturesEnabled,bindSettingsStore,createPluginServices,getStorageDir,isAdminAuthEnabled,isMcpStreamRequest,packageVersion,registerRoutes,renderEditorSidebarJs,renderEditorSidebarLoaderHtml,requireAccessLink,resolveAiAccessSettings});