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

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 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)}
1
+ "use strict";var mu=Object.create;var nr=Object.defineProperty;var gu=Object.getOwnPropertyDescriptor;var yu=Object.getOwnPropertyNames;var wu=Object.getPrototypeOf,bu=Object.prototype.hasOwnProperty;var a=(e,t)=>nr(e,"name",{value:t,configurable:!0});var W=(e,t)=>()=>(e&&(t=e(e=0)),t);var la=(e,t)=>{for(var n in t)nr(e,n,{get:t[n],enumerable:!0})},ca=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of yu(t))!bu.call(e,o)&&o!==n&&nr(e,o,{get:()=>t[o],enumerable:!(r=gu(t,o))||r.enumerable});return e};var rr=(e,t,n)=>(n=e!=null?mu(wu(e)):{},ca(t||!e||!e.__esModule?nr(n,"default",{value:e,enumerable:!0}):n,e)),vu=e=>ca(nr({},"__esModule",{value:!0}),e);var ku,R,y=W(()=>{"use strict";ku=a(()=>typeof document>"u"?new URL(`file:${__filename}`).href:document.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"?document.currentScript.src:new URL("main.js",document.baseURI).href,"getImportMetaUrl"),R=ku()});function Ts(e,t){let n=Rs.get(e);n?.current.rev!==t.rev&&Rs.set(e,{current:structuredClone(t),previous:n?.current})}function pa(e,t){let n=Rs.get(e);return n?.current.rev===t?n.previous:n?.current}function fa(e){if(ua.has(e))return;ua.add(e);let t=e.getFlows.bind(e);e.getFlows=async()=>{let n=await t();return Ts(e,n),n}}var Rs,ua,Ps=W(()=>{"use strict";y();Rs=new WeakMap,ua=new WeakSet;a(Ts,"recordLiveSnapshot");a(pa,"liveBeforeRevision");a(fa,"observeFlowSnapshots")});function St(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 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(`
3
+ `,o=Buffer.from(r,"utf8");if(o.byteLength>t)throw new he(n.kind,o.byteLength,t);return{bytes:o}}function ma(e,t){let n=(0,or.resolve)(t),r=(0,or.resolve)(e);if(r!==n&&!r.startsWith(n+or.sep))throw new Error("Path escapes .ai-access storage root");return r}function bn(e,t,n){let r=ma(t,e),o=`${r}.${process.pid}.tmp`;(0,ft.writeFileSync)(o,n.bytes,{mode:zr}),(0,ft.renameSync)(o,r),(0,ft.chmodSync)(r,zr)}function ga(e,t,n){let r=ma(t,e);(0,ft.appendFileSync)(r,n.bytes,{mode:zr});try{(0,ft.chmodSync)(r,zr)}catch{}}var ft,or,_s,zr,Y,ha,he,de=W(()=>{"use strict";y();ft=require("fs"),or=require("path"),_s=448,zr=384,Y=15e5,ha=8192,he=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(St,"encodeTrustedJson");a(ma,"assertUnderAiAccess");a(bn,"writeTrustedAtomic");a(ga,"appendTrusted")});function ir(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 Tu(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 wa(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=ir(t.get("origin")),d=i===void 0?void 0:e().find(c=>c===i),l=d!==void 0;if(Tu(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",Ru),t.method==="OPTIONS"&&t.get("access-control-request-method")){n.setHeader("Access-Control-Allow-Methods",xu),n.setHeader("Access-Control-Allow-Headers",Su),n.setHeader("Access-Control-Max-Age",String(600)),n.status(204).end();return}s=!1,r()}}function ba(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,xu,Su,Ru,Jr=W(()=>{"use strict";y();Au=/^\/mcp(?:\/?$|\/(?:a(?:\/|$)|api(?:\/|$)|rpc\/?$))/,xu="GET, POST, DELETE, OPTIONS",Su="Authorization, Content-Type, Accept, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID",Ru="Mcp-Session-Id, WWW-Authenticate, Retry-After, RateLimit, RateLimit-Policy, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset";a(ir,"canonicalOrigin");a(Tu,"appendVary");a(wa,"aiCorsMiddleware");a(ba,"installAiCorsMiddleware")});function Yr(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 dr(e){if(!Array.isArray(e))return;let t=[];for(let n of e){if(typeof n!="string")continue;let r=n.trim().toUpperCase();if(!(!Vr.test(r)||t.includes(r))&&(t.push(r),t.length>=ar))break}return t}var X,Wt,Vr,ar,vn=W(()=>{"use strict";y();X="[REDACTED]",Wt=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"]),Vr=/^[A-Za-z0-9_-]{2,40}$/,ar=100;a(Yr,"tokenizeName");a(dr,"normalizeSecretNamePatterns")});function _u(e){return e==="nodes"||e==="flows"||e==="full"}function Zr(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),_u(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=xn(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=ir(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=dr(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 Cu(e,t){if(!(0,cr.existsSync)(e))return{};try{let n=(0,cr.readFileSync)(e,"utf8");return Zr(JSON.parse(n),t)}catch(n){return t?.(`AI Access: config.json is corrupt or unreadable; defaults are used (${String(n)})`),{}}}function Xr(e,t){let n={},r={...lr};for(let s of Object.keys(lr).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]??lr[s]});let o={...r,sources:n};return e.oauth!==void 0||t.oauth!==void 0?(o.oauth={...kn,...t.oauth??{},...e.oauth??{}},n.oauth=e.oauth!==void 0?"settings.js":"disk"):(o.oauth={...kn},n.oauth="default"),o}function Is(e,t){let n=Zr(e??{},t);for(let o of Object.keys(lr).filter(s=>s!=="oauth"))e&&Object.hasOwn(e,o)&&n[o]===void 0&&Object.assign(n,{[o]:lr[o]});let r=e?.oauth;if(r&&typeof r=="object"&&!Array.isArray(r)){let o=n.oauth??={};for(let s of Object.keys(kn))Object.hasOwn(r,s)&&o[s]===void 0&&(t?.(`AI Access: settings.js aiAccess.oauth.${s} is invalid; using the default (${String(kn[s])})`),Object.assign(o,{[s]:kn[s]}))}return n}var cr,Es,Pu,kn,lr,An,Qr=W(()=>{"use strict";y();cr=require("fs"),Es=require("path");Re();Jr();vn();de();Pu=16384,kn={enabled:!1,accessTokenTtlSec:3600,refreshIdleTtlSec:2592e3,refreshMaxLifetimeSec:7776e3,dcrMaxClients:5},lr={enabled:!0,allowWithoutAdminAuth:!1,defaultLinkTtlSeconds:14400,proposalTtlDays:7,maxActiveLinks:10,defaultDeploymentType:"nodes",publicBaseUrl:"",corsAllowedOrigins:[],rateLimitWindowMs:6e4,rateLimitMaxPerIp:600,rateLimitMaxPerToken:300,secretNamePatterns:[...Wt],oauth:{...kn}};a(_u,"isDeploymentType");a(Zr,"sanitizePartial");a(Cu,"readDiskFile");a(Xr,"mergeAiAccessSettings");a(Is,"settingsJsPartial");An=class{constructor(t,n,r){this.paths=t;this.filePath=(0,Es.join)(t.root,"config.json"),this.settingsJs=Is(n,r),this.disk=Cu((0,cr.existsSync)(this.filePath)?this.filePath:(0,Es.join)(t.root,"settings.json"),r),this.cached=Xr(this.settingsJs,this.disk)}static{a(this,"SettingsStore")}filePath;disk;settingsJs;cached;getEffective(){return this.cached}getDisk(){return{...this.disk}}update(t){let n=Zr(t);for(let r of Object.keys(this.settingsJs))delete n[r];return this.persist(n),this.disk=n,this.cached=Xr(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=Zr(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=St(t,Pu,{kind:"settings",pretty:!0});bn(this.paths.root,this.filePath,n)}}});function to(e){eo=e}function va(){return eo}function ae(e){return e.adminAuth!=null&&e.adminAuth!==!1}function Me(e){return eo?eo.getEffective():Xr(Is(e.aiAccess),{})}function De(e){let t=Me(e);return t.enabled?ae(e)?!0:t.allowWithoutAdminAuth:!1}var eo,Te=W(()=>{"use strict";y();Qr();eo=null;a(to,"bindSettingsStore");a(va,"getSettingsStore");a(ae,"isAdminAuthEnabled");a(Me,"resolveAiAccessSettings");a(De,"areAiFeaturesEnabled")});function Pe(e){let t=`/${e??""}`.replace(/\/{2,}/g,"/");for(;t.length>1&&t.endsWith("/");)t=t.slice(0,-1);return t}function _e(e){let t=Pe(e);return t==="/"?"/mcp/api/v1":`${t}/mcp/api/v1`}function ur(e){let t=Pe(e);return t==="/"?"/ai-access":`${t}/ai-access`}function Rt(e){let t=Pe(e);return t==="/"?"/mcp/rpc":`${t}/mcp/rpc`}function Eu(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 Tt(e){if(e.auth)return e.auth.scopes.includes("propose")?"propose":e.auth.scopes.includes("read")?"read":null;let t=Eu(e);return t.includes("*")?"*":t.includes("read")?"read":null}function Iu(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 ka(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 Ce(e,t){let n="read";return(r,o,s)=>{if(!ae(e.settings)){s();return}let i=t==="api"?ka(o):null;e.auth.needsPermission(n)(r,o,d=>{if(i?.(),d){s(d);return}if(!r.user){if(t==="page"){o.redirect(Pe(e.settings.httpAdminRoot));return}o.status(401).json({error:"Authentication required",code:"auth_required"});return}if(!Tt(r)){o.status(401).json({error:"Insufficient permissions",code:"insufficient_permissions"});return}s()})}}function no(e,t){let n=ae(e.settings),r=Me(e.settings),o=De(e.settings),s=null;return n?s=Tt(t):o&&(s="*"),{adminAuthEnabled:n,aiFeaturesEnabled:o,allowWithoutAdminAuth:r.allowWithoutAdminAuth,permission:s,capabilities:Iu(s,o)}}function Sn(e){return(t,n,r)=>{if(!De(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 Rn(e){return(t,n,r)=>{if(!ae(e.settings)){if(De(e.settings)){r();return}n.status(403).json({error:"AI Access features are disabled",code:"ai_features_disabled"});return}if(Tt(t)!=="*"){n.status(403).json({error:"Full admin permission (*) is required for this action",code:"insufficient_permissions"});return}r()}}function Lu(e){return e.startsWith("aia_")}function Nu(e){return Lu(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 Aa(e,t,n,r){if(!ae(e.settings)){r();return}let o={error:He,code:ht},s=ka(n,o,ge);e.auth.needsPermission("read")(t,n,i=>{if(s(),i){r(i);return}if(!n.headersSent){if(!t.user||!Tt(t)){n.setHeader("WWW-Authenticate",ge),n.status(401).json(o);return}r()}})}function xa(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?(hr(t,r,l.link.kind),"wrong_kind"):(e.accessLink=l.link,"ok");if(l.status==="expired")return fr(e,t,o,l.expiresAt),"invalid";if(Nu(d)){t.setHeader("WWW-Authenticate",ge);let c=Fu(d);return t.status(401).json({error:zt,code:pr,...c?{hint:c}:{}}),"invalid"}return"continue"}function Sa(e,t,n="rest"){return(r,o,s)=>{if(!De(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=xa(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||Aa(e,r,o,s)}}function Ra(e,t,n="rest"){return(r,o,s)=>{let i=xa(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||Aa(e,r,o,s)}}var Ye=W(()=>{"use strict";y();Gt();Te();a(Pe,"normalizeHttpAdminRoot");a(_e,"mcpApiBase");a(ur,"dashboardPath");a(Rt,"mcpRpcPath");a(Eu,"scopesFromRequest");a(Tt,"resolvePermission");a(Iu,"capabilitiesFor");a(ka,"interceptNeedsPermissionUnauthorized");a(Ce,"requireAdminSession");a(no,"describeAccess");a(Sn,"requireAiFeatures");a(Rn,"requireStarPermission");a(Lu,"looksLikeAccessLinkToken");a(Nu,"looksLikeFailedAccessLinkAttempt");a(Fu,"truncatedTokenHint");a(Aa,"continueAsAdminSessionOrAccessLinkMissing");a(xa,"tryAccessLinkBearer");a(Sa,"requireAdminReadOrAccessLink");a(Ra,"requireAdminSessionOrAccessLink")});function Ou(e,t){let n=Pe(e);return`${n==="/"?"/mcp/a":`${n}/mcp/a`}/${t}`}function $u(e){return!!e.app.get("trust proxy")}function Ta(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 Mu(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 Kt(e){try{let n=new URL(`http://${e.includes(":")&&!e.startsWith("[")?`[${e}]`:e}`).hostname.replace(/^\[|\]$/g,"");if(!(0,Ls.isIP)(n))return;if(n.startsWith("::ffff:")){let r=n.slice(7);if((0,Ls.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 xn(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 ro(e){Ns=Mu(e)}function _a(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],!ju.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)||!Hu.test(t)&&!Du.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 Ea(e){let t=e?.trim().toLowerCase();return t==="http"||t==="https"?t:void 0}function Uu(e){let t=e.socket;return t?.encrypted?"https":t?"http":Ea(e.protocol)??"http"}function Fs(e){if(Ns)return Ns;let t=$u(e),n=t?Ta(e,"x-forwarded-host"):void 0,r=e.get("host")?.trim(),o=_a(n)?n:_a(r)?r:void 0;if(!o)return;let s=(t?Ea(Ta(e,"x-forwarded-proto")):void 0)??Uu(e);try{return new URL(`${s}://${o}`).origin}catch{return}}function mr(e,t){let n=Fs(e);return n?`${n}${t}`:t}function Ia(e,t,n){return mr(e,Ou(t,n))}function Os(e){return e.replace(/\/mcp\/a\/[^/?#]+/gi,"/mcp/a/<redacted>")}var Ls,Tn,Ca,Ns,Pa,Du,Hu,ju,Re=W(()=>{"use strict";y();Ls=require("net");Ye();Tn=8192,Ca=1800*1e3;a(Ou,"mcpAccessLinkPath");a($u,"isTrustProxyEnabled");a(Ta,"firstForwardedValue");a(Mu,"normalizePublicBaseUrl");a(Kt,"canonicalizeIp");a(xn,"validateSettingsPublicBaseUrl");a(ro,"bindPublicBaseUrl");Pa="[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?",Du=new RegExp(`^${Pa}(?:\\.${Pa})*$`),Hu=/^(?:(?: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)$/,ju=/^\[[0-9A-Fa-f:.]+\]$/;a(_a,"isValidHostHeader");a(Ea,"validScheme");a(Uu,"connectionScheme");a(Fs,"requestPublicOrigin");a(mr,"absolutizePath");a(Ia,"buildAccessLinkUrl");a(Os,"redactAccessLinkTokenInPath")});function Ku(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 $s(e,t,n,r){e.setHeader("WWW-Authenticate",ge),e.status(401).json({error:t,code:n,...r?{hint:r}:{}})}function Ju(e){let t=e??"";for(;t.endsWith("/");)t=t.slice(0,-1);return`${t}/ai-access#links`}function Vu(e,t,n,r){let o=mr(e,Ju(t));return{error:Bu,code:Wu,...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 fr(e,t,n,r,o){t.setHeader("WWW-Authenticate",ge),t.status(401).json(Vu(e,n,r,o))}function Yu(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 oo(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 Xu(e,t){let n=Zu;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: ${oo("get_link")}`:e==="rest"?`This REST endpoint requires a rest token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${oo("rest")}`:`This MCP endpoint requires an mcp token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${oo("mcp")}`}function hr(e,t,n){e.setHeader("WWW-Authenticate",ge),e.status(403).json({error:`Access link kind mismatch: expected ${t}, got ${n}`,code:Gu,expectedKind:t,actualKind:n,hint:Xu(t,n),tellUser:oo(t)})}function Pn(e,t,n){return(r,o,s)=>{if(!De(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=Ku(r.headers.authorization);if(i.kind==="basic"){$s(o,qu,zu);return}if(i.kind==="missing"||i.kind==="other"){$s(o,He,ht);return}let d=t.validateResult(i.token);if(d.status==="expired"){fr(r,o,e.settings.httpAdminRoot,d.expiresAt);return}if(d.status!=="active"){$s(o,zt,pr,Yu(i.token));return}if(d.link.kind!==n){hr(o,n,d.link.kind);return}r.accessLink=d.link,s()}}var zt,Bu,He,qu,ge,ht,pr,Wu,zu,Gu,Zu,Gt=W(()=>{"use strict";y();Re();Te();zt="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.",Bu="Access link token has expired. Ask the user for a new access link.",He="Access link Bearer token is missing. Ask the user for a new access link and send Authorization: Bearer <token>.",qu="Authorization must use the Bearer scheme (Authorization: Bearer <access-link-token>), not Basic. Ask the user for a new access link.",ge='Bearer realm="node-red-ai-access", charset="UTF-8"',ht="access_link_missing",pr="access_link_invalid",Wu="token_expired",zu="bearer_scheme_required",Gu="wrong_token_kind";a(Ku,"parseAuthorizationHeader");a($s,"sendUnauthorized");a(Ju,"accessLinksPagePath");a(Vu,"tokenExpiredBody");a(fr,"sendTokenExpired");a(Yu,"truncatedBearerHint");Zu="ask the owner: Node-RED /ai-access \u2192 Access tokens \u2192 create kind=rest/mcp (or get_link)";a(oo,"whatToTellUserWrongTokenKind");a(Xu,"wrongTokenKindHint");a(hr,"sendWrongTokenKind");a(Pn,"requireAccessLink")});var re,te,je,Ze,io,Ee=W(()=>{"use strict";y();re=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}},te=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}},je=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"}},io="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 Xe(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}var ao=W(()=>{"use strict";y();a(Xe,"conflictFieldValue")});function Qe(e,t){if(!e||!Qu.test(e)||e.includes(".."))throw new Error(`Invalid ${t}`);return e}function lo(e){if(!e||!ep.test(e)||e.includes(".."))throw new Error("Invalid proposal id");return e}function Ms(e,t){let n=(0,Vt.basename)(t);if(!n||n!==t||n.includes(".."))throw new Error("Invalid file name");let r=(0,Vt.resolve)(e),o=(0,Vt.resolve)(r,n);if(o!==r&&!o.startsWith(r+Vt.sep))throw new Error("Path escapes storage directory");return o}function Jt(e,t=1e5){if(typeof e=="string")return e.replace(/\0/g,"").slice(0,t)}function Ue(e){let t=e&&typeof e=="object"&&!Array.isArray(e)?e:{},n={};for(let o of tp){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=Jt(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>Y)throw new he(`flow.${o}`,d,Y);n[o]=JSON.parse(i)}let r=Buffer.byteLength(JSON.stringify(n),"utf8");if(r>Y)throw new he("flow",r,Y);return n}function La(e){let t={};Array.isArray(e.skippedAttempts)&&(t.skippedAttempts=e.skippedAttempts.map(n=>{let r=n;return{proposalId:Jt(r.proposalId,200),attemptId:Jt(r.attemptId,200),flowId:Jt(r.flowId,200)}})),Array.isArray(e.changedFlowIds)&&(t.changedFlowIds=e.changedFlowIds.map(n=>Jt(n,200)).filter(n=>n!==void 0)),Array.isArray(e.affectedProposalIds)&&(t.affectedProposalIds=e.affectedProposalIds.map(n=>Jt(n,200)).filter(n=>n!==void 0)),typeof e.rejectedWhileDeployUnknown=="boolean"&&(t.rejectedWhileDeployUnknown=e.rejectedWhileDeployUnknown);for(let n of np){if(!(n in e))continue;let r=e[n];if(rp.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=Jt(r,2e3);o!==void 0&&(t[n]=o)}return t}var Vt,Qu,ep,tp,np,rp,co=W(()=>{"use strict";y();Vt=require("path");de();Qu=/^[A-Za-z0-9_.-]+$/,ep=/^prop-[A-Za-z0-9_-]+$/,tp=["id","type","label","disabled","info","env"],np=["proposalId","attemptId","withdrawalAuthority","originalOwnerId","flowId","expectedRev","resultRev","reason","code","currentRev","title","displayName","flowLabel","linkId","label","expiresAt","createdAt","ttlSeconds","kind","redactions","restoredRedactions","operation"],rp=new Set(["ttlSeconds","redactions","restoredRedactions"]);a(Qe,"assertSafeSegment");a(lo,"assertSafeProposalId");a(Ms,"resolveUnderDir");a(Jt,"sanitizeString");a(Ue,"sanitizeFlowTab");a(La,"sanitizeAuditDetails")});function U(e){return e==="pending"||e==="conflict"}function fo(e,t){return`oauth-${(0,gr.createHash)("sha256").update(`${e}\0${t}`).digest("hex")}`}function mt(e){return e.kind==="oauth"?fo(e.clientId,e.user):e.linkId}function op(e){return e==="pending"||e==="conflict"||e==="approved"||e==="rejected"||e==="failed"||e==="withdrawn"||e==="expired"||e==="superseded"}function Ie(e,t){return typeof e=="string"&&e.length>0?e.replace(/\0/g,"").slice(0,t):void 0}function ip(e){if(!e||typeof e!="object"||Array.isArray(e))return;let t=e,n=Ie(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=Ie(l.kind,64),u=Ie(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=Ie(l.nodeId,200);f&&(p.nodeId=f);let h=Ie(l.nodeType,200);h&&(p.nodeType=h);let m=Ie(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:Ie(t.checkedAt,64)??""},i=Ie(t.mergedHash,64);return i&&/^[0-9a-f]+$/.test(i)&&(s.mergedHash=i),s}function ap(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 Ds(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e.slice(0,sp)){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 Na(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 gr,Pt,Fa,Oa,uo,ye,Q,_t,Le,sp,et,po,tt=W(()=>{"use strict";y();gr=require("crypto"),Pt=require("fs"),Fa=require("path"),Oa=require("util");ao();co();de();uo=(0,gr.randomBytes)(16).toString("hex");a(U,"isOpenProposalStatus");a(fo,"oauthOwnerId");a(mt,"proposalOwnerId");ye=class extends Error{static{a(this,"ProposalNotFoundError")}code="proposal_not_found";statusCode=404;constructor(t){super(`Proposal '${t}' was not found`),this.name="ProposalNotFoundError"}},Q=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"}},_t=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"}},Le=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(op,"isProposalStatus");sp=50;a(Ie,"cleanText");a(ip,"sanitizeRebaseInfo");et=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(ap,"sanitizeRestoredSecrets");a(Ds,"sanitizeProposalWarnings");a(Na,"sanitizeAiOverrides");po=class{constructor(t,n={}){this.paths=t;this.now=n.now??(()=>Date.now()),this.randomId=n.randomId??(()=>(0,gr.randomBytes)(12).toString("hex")),this.maxProposalBytes=n.maxProposalBytes??Y}static{a(this,"ProposalStore")}inFlight=new Set;now;randomId;maxProposalBytes;fileFor(t){let n;try{n=lo(t)}catch{throw new ye(t)}return Ms(this.paths.proposals,`${n}.json`)}toDiskRecord(t){let n=lo(t.id),r=Qe(String(t.expectedRev).slice(0,200),"expectedRev"),o=Qe(String(t.payload.flowId).slice(0,200),"flowId"),s=Ue(t.payload.flow),i=t.baseFlow!==void 0?Ue(t.baseFlow):void 0,d=t.createdBy.kind==="oauth"?{kind:"oauth",clientId:Qe(String(t.createdBy.clientId).slice(0,200),"clientId"),user:String(t.createdBy.user).replace(/\0/g,"").slice(0,200)}:{kind:"access-link",linkId:Qe(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:op(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=Ds(t.warnings);c&&(l.warnings=c),t.landedAfterWithdraw===!0&&(l.landedAfterWithdraw=!0),typeof t.deploymentAlert=="string"&&(l.deploymentAlert=Ie(t.deploymentAlert,500)??"");let u=ap(t.restoredSecrets);if(u&&(l.restoredSecrets=u),t.deployPending?.bootId===uo){let m=Ie(t.deployPending.mergedHash,64),g=Ie(t.deployPending.reason,500);if(m&&/^[0-9a-f]+$/.test(m)&&g){l.deployPending={mergedHash:m,reason:g,bootId:uo};let w=Ie(t.deployPending.attemptId,36);w&&(l.deployPending.attemptId=w),t.deployPending.queueReleased===!0&&(l.deployPending.queueReleased=!0)}}let p=Na(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:Ue(f.undo.baseFlow),flow:Ue(f.undo.flow),warnings:Ds(f.undo.warnings)??[],aiConflictOverrides:Na(f.undo.aiConflictOverrides)??[]}});let h=ip(t.rebase);if((h&&U(l.status)||h&&h.state!=="conflict")&&(l.rebase=h),t.decidedBy)if(t.decidedBy.kind==="access-link"){let m={kind:"access-link",linkId:Qe(String(t.decidedBy.linkId).slice(0,200),"linkId")};typeof t.decidedBy.label=="string"&&(m.label=t.decidedBy.label.replace(/\0/g,"").slice(0,200)),l.decidedBy=m}else{let m={kind:"admin"};typeof t.decidedBy.username=="string"&&(m.username=t.decidedBy.username.replace(/\0/g,"").slice(0,200)),l.decidedBy=m}return typeof t.reason=="string"&&(l.reason=t.reason.replace(/\0/g,"").slice(0,2e3)),t.rejectedWhileDeployUnknown===!0&&(l.rejectedWhileDeployUnknown=!0),typeof t.resultRev=="string"&&(l.resultRev=Qe(t.resultRev.slice(0,200),"resultRev")),l}writeAtomic(t,n){let r=this.fileFor(t),o=this.toDiskRecord(n),s=St(o,this.maxProposalBytes,{kind:"proposal",pretty:!0});bn(this.paths.root,r,s)}create(t){let n=new Date(this.now()).toISOString(),r=lo(`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=Ds(t.warnings);return s&&(o.warnings=s),t.restoredSecrets?.length&&(o.restoredSecrets=t.restoredSecrets),this.writeAtomic(r,o),this.get(r)}currentProcessProposal(t){return t.deployPending?.bootId!==uo&&delete t.deployPending,t}get(t){let n=this.fileFor(t);if(!(0,Pt.existsSync)(n))throw new ye(t);return this.currentProcessProposal(JSON.parse((0,Pt.readFileSync)(n,"utf8")))}list(t){if(!(0,Pt.existsSync)(this.paths.proposals))return[];let n=(0,Pt.readdirSync)(this.paths.proposals).filter(o=>o.endsWith(".json")),r=[];for(let o of n){let s=(0,Fa.basename)(o);if(!(s!==o||s.includes("..")))try{let i=JSON.parse((0,Pt.readFileSync)(Ms(this.paths.proposals,s),"utf8"));if(t?.linkId&&mt(i.createdBy)!==t.linkId||t?.status&&i.status!==t.status||t?.flowId&&i.payload.flowId!==t.flowId)continue;r.push(this.currentProcessProposal(i))}catch{}}return r.sort((o,s)=>o.createdAt<s.createdAt?1:-1)}beginApprove(t){if(this.inFlight.has(t))throw new Le(t);let n=this.get(t);if(!U(n.status))throw new Q(t,n.status);return this.inFlight.add(t),n}releaseLock(t){this.inFlight.delete(t)}recordDeployPending(t,n){let r=this.get(t);if(n&&!U(r.status))throw new Q(t,r.status);return n?r.deployPending={...n,bootId:uo}:delete r.deployPending,U(r.status)&&(r.status="pending"),r.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,r),this.get(t)}flagRejectedWhileDeployUnknown(t){let n=this.get(t);return n.rejectedWhileDeployUnknown=!0,this.writeAtomic(t,n),this.get(t)}flagLandedAfterWithdraw(t){let n=this.get(t);n.landedAfterWithdraw=!0,this.writeAtomic(t,n)}flagDeployment(t,n){let r=this.get(t);r.deploymentAlert=n,this.writeAtomic(t,r)}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 Le(t);let o=this.get(t);return o.rejectedWhileDeployUnknown=o.deployPending?.queueReleased===!0,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 Le(t);let o=this.get(t);if(!U(o.status))throw new Q(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 Q(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 Q(t,r.status);if(this.inFlight.has(t))throw new Le(t);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);let o=a((i,d)=>d===r.payload.flowId?i:[...i.nodes??[],...i.configs??[]].find(l=>l.id===d),"target"),s=n.aiConflictOverrides??(r.aiConflictOverrides??[]).map(i=>({nodeId:i.nodeId,fields:i.fields.filter(d=>{let l=o(n.baseFlow,i.nodeId),c=o(n.flow,i.nodeId);return!(0,Oa.isDeepStrictEqual)(d?Xe(l,d):l,d?Xe(c,d):c)})})).filter(i=>i.fields.length>0);return s.length?r.aiConflictOverrides=s: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 $a(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 ho(e,t,n){let r=e.find(d=>d.id===t);if(!r||r.type!=="tab"&&r.type!=="subflow")throw new te(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({...$a(l,`flowData.nodes[${d}]`),z:t});for(let[d,l]of(n.configs??[]).entries())s.push({...$a(l,`flowData.configs[${d}]`),z:t});return[...e.filter(d=>d.id!==t&&d.z!==t),o,...s]}function yr(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 _n(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 te(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 te(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 mo=W(()=>{"use strict";y();Ee();a($a,"asFlowNode");a(ho,"replaceFlowTabInFlatFlows");a(yr,"assertFlowsSnapshot");a(_n,"normalizeFlowTab")});function Ma(e,t=new Hs){return go in e?e:{kind:e.kind,[go]: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 js(e){return go in e?e[go]:void 0}function yo(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 te(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 dp,Hs,go,Us=W(()=>{"use strict";y();Ee();dp=24,Hs=class{constructor(t=dp){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)}},go=Symbol.for("ai-access.revHistory");a(Ma,"withRevHistory");a(js,"revHistoryOf");a(yo,"snapshotAdapter")});function zs(e){return e?lp.has(e)?!0:e.startsWith("subflow:"):!1}function Ct(e,t="node.type"){if(e==null||e==="")throw new x(`${t} is required`,"missing_node_type");if(typeof e!="string")throw new x(`${t} must be a non-empty string (got ${typeof e})`,"invalid_node_type",{details:{type:e,got:typeof e}});if(!e.trim())throw new x(`${t} is required`,"missing_node_type");return e}function gp(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 yp(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 bp(e){return Math.max(2,Math.floor(e.length/3))}function Gs(e,t,n=5){if(typeof e!="string"||!e)return[];let r=bp(e);return t.filter(s=>typeof s=="string"&&s!==e&&!wp.has(s)&&!s.startsWith("_")&&!s.startsWith("subflow:")).map(s=>({c:s,dist:gp(e,s),prefix:yp(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 vp(e){return e.endsWith("/")?e.slice(0,-1):e}function Wa(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,qa.createRequire)(e)}function kp(){try{let t=Wa()("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}function za(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 Ks(e=mp,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 Ga(e={}){if(kp())try{let n=Wa()("@node-red/runtime");if(n.nodes&&typeof n.nodes.getNodeList=="function")return new Ws(n.nodes)}catch{}if(e.baseUrl)return new qs({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 Js(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 bo(e,t={}){let n=t.limit??cp,{byType:r}=await Js(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 xp(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 Ha(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 Sp(e,t=2e3){let n=Ha(e,"script");return n=Ha(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 Rp(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:ja(e,d,i)}return ja(e,n.index,r)}catch{return null}}function ja(e,t,n){let r=e.slice(t),o=r.indexOf("{");if(o<0)return{type:n};let s=wr(r.slice(o));if(!s)return{type:n};let i=Ua(s,"category"),d=Ua(s,"color"),l=Ba(s,"inputs"),c=Ba(s,"outputs"),u=Pp(s),p=[];if(u){for(let g of Object.values(u))if(g&&typeof g=="object"&&!Array.isArray(g)){let w=g.type;typeof w=="string"&&w.length>0&&p.push(w)}}let f,h=xp(e,n);h&&(f=Sp(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 Tp(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 wr(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 Ua(e,t){let n=new RegExp(`\\b${t}\\s*:\\s*['"]([^'"]+)['"]`);return e.match(n)?.[1]}function Ba(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=wr(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=wr(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=Ep(h)}s[l]=u}return Object.keys(s).length>0?s:void 0}function _p(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 Cp(e){return e==="_DEFAULT_"?"":e}function wo(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=_p(t,o+1);s+=d.char,o=d.next;continue}if(i===r)break;s+=i,o+=1}return Cp(s)}if(t.startsWith("[")){let r=Ka(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=wo(d);s.push(l);let c=Bs(d);i+=c>0?c:1}return s}if(t.startsWith("{")){let r=wr(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=wo(o.slice(i));d=typeof c=="string"?c:"",i+=Bs(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]=wo(l),i+=Bs(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 Bs(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=Ka(t);return n+(o?.length??1)}if(t.startsWith("{")){let o=wr(t);return n+(o?.length??1)}let r=/^(?:-?\d+(?:\.\d+)?|true|false|null)/.exec(t);return r?n+r[0].length:n+1}function Ka(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 Ep(e){return wo(e)}function Ip(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 Vs(e,t){let n=e.toLowerCase();return t.includes(e)?e:t.find(r=>r.toLowerCase()===n)}async function Yt(e,t,n={}){if(!t||typeof t!="string")throw new x("Missing node type","missing_node_type");let r=n.includeHelp!==!1;if(zs(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=Ip(o,t);if(!i){let l=Vs(t,s);throw l&&l!==t?new x(`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 x(`Node type '${t}' was not found`,"node_type_not_found",{statusCode:404,details:{type:t,nearest:Gs(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=Rp(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=Tp(c.help)))}}catch{}return d}async function Ja(e,t){let{allNames:n}=await Js(e),r=new Set(n),o=new Set,s=a(l=>{let c=Ct(l,"node.type");if(o.has(c)||zs(c)){o.add(c);return}if(o.add(c),r.has(c))return;let u=Vs(c,n);throw u&&u!==c?new x(`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 x(`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:Gs(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 Va(e,t){let n=Ct(t,"node.type");if(zs(n))return;let{allNames:r}=await Js(e);if(r.includes(n))return;let o=Vs(n,r);throw o&&o!==n?new x(`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 x(`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:Gs(n,r,5)}})}async function Lp(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 Np(e,t){let n=Date.now();if(Be&&n-Be.fetchedAt<hp)return Be.body;let r=Be?.etag,o=await e(t,r);if(o.status===304&&Be)return Be={...Be,fetchedAt:n,...o.etag?{etag:o.etag}:{}},Be.body;if(!o.body||!Array.isArray(o.body.modules)){if(Be)return Be.body;throw new x("Node-RED catalogue unavailable","catalogue_unavailable",{statusCode:502})}return Be={fetchedAt:n,body:o.body,...o.etag?{etag:o.etag}:{}},o.body}async function vo(e,t,n={}){let r=t.trim();if(!r)throw new x("q query parameter is required","missing_query");let o=n.limit??up,s=n.catalogueUrl??fp,i=n.catalogueFetcher??Lp,d=await Np(i,s),l=Array.isArray(d.modules)?d.modules:[],c=await e.listNodeSets(),u=new Set(c.flatMap(g=>{let w=[g.module,g.id];return g.id.includes("/")&&w.push(g.id.split("/")[0]??g.module),w.map(v=>v.toLowerCase())})),p=r.toLowerCase(),f=[];for(let g of l){let w=g.id.toLowerCase(),v=(g.keywords??[]).map(O=>String(O).toLowerCase()),k=(g.description??"").toLowerCase(),$=Fp(p,w,v,k);if($<=0)continue;let C={module:g.id,installed:u.has(w)};typeof g.description=="string"&&(C.description=g.description),typeof g.version=="string"&&(C.version=g.version),typeof g.updated_at=="string"&&(C.updated=g.updated_at),typeof g.downloads=="number"&&(C.downloads=g.downloads),typeof g.url=="string"&&(C.link=g.url),f.push({item:C,score:$})}f.sort((g,w)=>w.score-g.score||(w.item.downloads??0)-(g.item.downloads??0)||g.item.module.localeCompare(w.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:pp}}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 qa,lp,cp,up,pp,fp,hp,mp,wp,qs,Da,Ws,Be,Zt=W(()=>{"use strict";y();qa=require("module");le();lp=new Set(["tab","subflow","group","junction"]),cp=100,up=30,pp="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.",fp="https://catalogue.nodered.org/catalogue.json",hp=3600*1e3,mp=["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(zs,"isStructuralNodeType");a(Ct,"requireNodeTypeName");a(gp,"editDistance");a(yp,"sharedPrefixLength");wp=new Set(["unknown","tab","subflow","group"]);a(bp,"nearestDistanceCutoff");a(Gs,"nearestNodeTypes");a(vp,"trimTrailingSlash");a(Wa,"createPluginRequire");a(kp,"isRuntimeSettingsAvailable");a(za,"normalizeNodeSet");a(Ks,"createStaticNodeTypesAdapter");qs=class{static{a(this,"HttpNodeTypesAdapter")}baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=vp(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=za(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()}},Da={user:null,req:null},Ws=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(Da))}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=za(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",...Da}));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(Ga,"createNodeTypesAdapter");a(Js,"flattenInstalledTypes");a(Ap,"matchesQuery");a(bo,"listNodeTypes");a(xp,"extractHelpScriptBody");a(Ha,"removeHtmlBlocks");a(Sp,"stripHelpText");a(Rp,"parseRegisterTypeFromHtml");a(ja,"parseRegisterTypeAt");a(Tp,"splitHelpSections");a(wr,"extractBalancedObject");a(Ua,"matchStringProp");a(Ba,"matchNumberProp");a(Pp,"parseDefaultsBlock");a(_p,"unescapeJsStringEscape");a(Cp,"normalizeDefaultValue");a(wo,"parseJsLiteral");a(Bs,"measureJsLiteralLength");a(Ka,"extractBalancedArray");a(Ep,"parseLooseValue");a(Ip,"findSetForType");a(Vs,"findCaseInsensitiveType");a(Yt,"getNodeTypeDetail");a(Ja,"assertKnownNodeTypes");a(Va,"assertKnownNodeType");Be=null;a(Lp,"defaultCatalogueFetcher");a(Np,"loadCatalogue");a(vo,"searchPalette");a(Fp,"paletteMatchScore")});function gt(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 x(`${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 x(t.error,t.code,{statusCode:o,details:r})}function Ys(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 Op(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 Za(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 ${Op(c)}; node ids must be unique across all flows`,nodeId:l})}return o}function Ya(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 Mp(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]);for(let r of n)if(!$p.has(r)&&JSON.stringify(e[r])!==JSON.stringify(t[r]))return!0;return!1}function Dp(e,t){let n=Ya(t);if(!e)return new Set(n.keys());let r=Ya(e),o=new Set;for(let[s,i]of n){let d=r.get(s);(!d||Mp(d,i))&&o.add(s)}return o}function Xa(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(...Ys(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{Ct(c.type,"node.type")}catch(u){u instanceof x&&n.push({code:u.code,error:u.message,...typeof c.id=="string"?{nodeId:c.id}:{}})}"wires"in c&&Cn(c.wires,n,typeof c.id=="string"?c.id:void 0)}return n}function Cn(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 Hp(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 Yt(t,n,{includeHelp:!1})).outputs}catch{return}}function jp(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 Up(e){return Array.isArray(e)&&e.some(t=>typeof t=="string"&&t.length>0)}function Bp(e,t){return t<=0?!1:!("wires"in e)||e.wires===void 0?!0:Array.isArray(e.wires)?!e.wires.some(Up):!1}async function Zs(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 Hp(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(!Cn(d.wires,r,d.id))continue;let p=d.wires;if(l!==void 0){let f=jp(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&&Bp(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 Xs(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 qp(e,t){return e.filter(n=>n.type===t).map(n=>n.id)}async function Qs(e,t,n){let r=[],o=[...t];for(let s of e){let i;try{i=Ct(s.type,"node.type")}catch{continue}let d;try{d=await Yt(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],w=qp(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:w});continue}o.find(k=>k.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:w})}}return r}async function Qa(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(...Xs(r,n));let d=t.baseFlow?Dp(t.baseFlow,e):void 0,l=await Zs(r,t.nodeTypes,{...d?{softWarningNodeIds:d}:{}});if(s.push(...l.errors),i.push(...l.warnings),t.nodeTypes){let c=await Qs(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 $p,ei=W(()=>{"use strict";y();le();Zt();a(gt,"throwValidationIssues");a(Ys,"collectDuplicateNodeIdIssues");a(Op,"describeLiveNode");a(Za,"collectCrossFlowDuplicateIssues");$p=new Set(["x","y","w","h","g","id","z"]);a(Ya,"collectNodesMap");a(Mp,"nodeContentChanged");a(Dp,"changedNodeIds");a(Xa,"collectFullFlowShapeIssues");a(Cn,"assertValidWiresShape");a(Hp,"resolveNodeOutputCount");a(jp,"wireCountIssue");a(Up,"portHasTargets");a(Bp,"nodeIsUnwired");a(Zs,"collectWireValidation");a(Xs,"collectZMismatchIssues");a(qp,"configCandidates");a(Qs,"collectConfigWarnings");a(Qa,"validateFlowTab")});function Et(e,t="node"){return typeof e=="string"&&e.trim()?e.trim():t}function ko(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 td(e,t){e[ed]=t}function Gp(e){if(!e)return;let t=e[ed];return t instanceof Map?t:void 0}function Kp(e){return typeof e!="string"||!e.trim()?new Set:new Set(e.split(",").map(t=>t.trim()).filter(Boolean))}function Jp(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 ve(e){let t=Kp(e.include);return Jp(e.include_payload)&&(t.add("payload"),t.add("baseFlow")),t}function Z(e,t){let n={req:e};t!==void 0&&(n.httpAdminRoot=t);let r=Gp(e);return r&&(n.liveFlowLabels=r),n}function En(e){return typeof e.name=="string"&&e.name.trim()?e.name.trim():typeof e.label=="string"&&e.label.trim()?e.label.trim():""}function br(e){return`"${e.replace(/"/g,"'")}"`}function In(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 Vp(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 Yp(e,t){let n=En(e),r=En(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 ti(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&&Vp(d,i)&&o.push({before:d,after:i})}return{added:n,removed:r,modified:o}}function Ao(e){return e===1?"node":"nodes"}function Zp(e){return e.length===1&&e[0]?`adds 1 ${Et(e[0].type)}`:`adds ${e.length} ${Ao(e.length)}`}function Xp(e){return`deletes ${e} ${Ao(e)}`}function Qp(e){return`edits ${e} ${Ao(e)}`}function ef(e){let t=[];e.removed>0&&t.push(Xp(e.removed));let n=e.added??[],r=e.addedCount??n.length;r>0&&t.push(n.length===r?Zp(n):`adds ${r} ${Ao(r)}`),(e.modified??0)>0&&t.push(Qp(e.modified??0));let o=t.length?t.join(", "):"replaces nodes",s=e.flowLabel?.trim();return s?`Replace ${s}: ${o}`:`Replace flow: ${o}`}function tf(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=Et(u.type),f=En(u);return f?`Add ${p} node ${br(f)}`:`Add ${p} node`}if(n[0]){let u=n[0],p=Et(u.type),f=En(u);return f?`Remove ${p} node ${br(f)}`:`Remove ${p} node`}let s=r[0];if(!s)return;let i=Et(s.after.type,Et(s.before.type)),d=En(s.before),l=En(s.after);if(Yp(s.before,s.after)&&d&&l)return`Rename ${i} node ${br(d)} \u2192 ${br(l)}`;let c=l||d;return c?`Edit ${i} ${br(c)}`:`Edit ${i}`}function ni(e,t){return typeof e?.label=="string"&&e.label.trim()?e.label.trim():typeof t?.label=="string"&&t.label.trim()?t.label.trim():""}function xo(e,t){if(typeof e.flowLabel=="string"&&e.flowLabel.trim())return e.flowLabel.trim();let n=ni(e.payload?.flow,e.baseFlow);if(n)return n;let r=e.payload?.flowId;return r&&t?.has(r)?t.get(r)??"":""}function nd(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 ri(e,t){let n=ti(In(e??{}),In(t)),r=n.added.length+n.removed.length+n.modified.length,o=ni(t,e);if(r===0)return o?`Update ${o}`:"Update flow";if(n.removed.length>0)return ef({...o?{flowLabel:o}:{},removed:n.removed.length,added:n.added,modified:n.modified.length});if(r>zp)return o?`${r} changes in ${o}`:`${r} changes`;let s=tf(n)??(o?`Update ${o}`:"Update flow");return nd(s,o)}function nf(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 oi(e,t){return`${e} \xB7 ${of(t)}`}function rf(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 of(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=rf(-n.getTimezoneOffset());return`${r} ${s} ${i}:${d} ${l}`}function vr(e){if(typeof e!="string")return;let t=e.replace(/\0/g,"").replace(/[\u0001-\u001F\u007F]/g," ").replace(/\s+/g," ").trim().slice(0,Wp).trim();return t.length>0?t:void 0}function sf(e,t){let n=Pe(t);return`${n==="/"?"/ai-access":`${n}/ai-access`}#proposal=${encodeURIComponent(e)}`}function si(e,t={}){let n=xo(e,t.liveFlowLabels),r=vr(e.title),o=ri(e.baseFlow,n&&!ni(e.payload.flow,e.baseFlow)?{...e.payload.flow,label:n}:e.payload.flow),s=ti(In(e.baseFlow??{}),In(e.payload.flow)),i=nf(r,s.removed.length)?o:r??o;return nd(i,n)}function af(e){return typeof e.storedTitle=="string"&&e.storedTitle?e.storedTitle:vr(e.title)}function ce(e,t={}){let n=si(e,t),r=xo(e,t.liveFlowLabels),o=af(e)??n,s=sf(e.id,t.httpAdminRoot),i=t.req?mr(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:oi(n,e.createdAt),reviewUrl:i,reviewPath:s}}function df(e){let t=ti(In(e.baseFlow??{}),In(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 lf(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 ue(e,t={}){let n=ce(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:lf(n.createdBy),changeSummary:df(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.deploymentAlert&&(o.deploymentAlert=n.deploymentAlert),n.rejectedWhileDeployUnknown&&(o.rejectedWhileDeployUnknown=!0),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 rd(e,t={}){return e.map(n=>ue(n,t))}var Wp,zp,ed,qe=W(()=>{"use strict";y();Re();Ye();Wp=80,zp=1;a(Et,"nodeTypeText");ed=Symbol.for("ai-access.liveFlowLabels");a(ko,"liveFlowLabelsFromFlows");a(td,"setRequestFlowLabels");a(Gp,"getRequestFlowLabels");a(Kp,"parseIncludeQuery");a(Jp,"isTruthyFlag");a(ve,"resolveIncludeFromQuery");a(Z,"presentOptionsFor");a(En,"nodeLabel");a(br,"quoteName");a(In,"collectNodes");a(Vp,"nodeContentChanged");a(Yp,"isRenameOnly");a(ti,"classifyFlowChanges");a(Ao,"pluralNodes");a(Zp,"describeAddedClause");a(Xp,"describeRemovedClause");a(Qp,"describeModifiedClause");a(ef,"buildReplaceFlowTitle");a(tf,"describeOneChange");a(ni,"flowLabelHint");a(xo,"resolveFlowLabel");a(nd,"ensureTitleIncludesFlowName");a(ri,"autoGenerateProposalTitle");a(nf,"shouldRefreshDisplayTitle");a(oi,"buildProposalDisplayName");a(rf,"formatUtcOffset");a(of,"formatProposalSubmittedAt");a(vr,"sanitizeProposalTitle");a(sf,"buildProposalReviewPath");a(si,"resolveProposalTitle");a(af,"storedTitleOf");a(ce,"presentProposal");a(df,"buildChangeSummary");a(lf,"presentCreatedByForApi");a(ue,"presentProposalForApi");a(rd,"presentProposalsForApi")});function Rr(e){let t=e.join("\0"),n=So.get(t);if(n)return n;let r=[...new Set(e.map(d=>Yr(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=Yr(d),c=l[l.length-1];if(!c||cf.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"),So.size>32&&So.clear(),So.set(t,s),s}function It(){return va()?.getEffective()?.secretNamePatterns??Wt}function Ar(e){let t=e.trim();return!(!t||t===X||uf.has(t.toLowerCase())||/^\$\{[A-Za-z0-9_]+\}$/.test(t))}function ff(e){let t=id.get(e);return t||(t=hf(e),id.set(e,t)),t}function hf(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!==X,"accept")},{hints:["?","&"],re:/[?&](?<k>[A-Za-z_][\w.-]{0,63})=(?<s>[^&#\s"'`<>]{1,2048})/dg,accept:a((t,n)=>e.matches(n.groups?.k??"")&&Ar(bf(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:Ar(t)},"accept")}]}function gf(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 yf(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||gf(e)>=3}function wf(e){let t=e.patterns.filter(r=>mf.includes(r.toUpperCase())),n=Rr(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??"")&&Ar(r)&&!r.startsWith("$")&&!r.startsWith("{{")&&(n.matches(o.groups?.k??"")||yf(r))&&!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/.test(r)&&o.input.charAt(o.indices?.groups?.s?.[1]??0)!=="(","accept")}}function bf(e){try{return decodeURIComponent(e)}catch{return e}}function vf(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 kf(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=vf(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)||Ar(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=ff(t);if(!n.code){let u=ad.get(t);u||(u=wf(t),ad.set(t,u)),o=[...o,u]}r.push(...kf(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!==X&&(i+=e.slice(d,u.start)+X,l.push(p),c.push(u),d=u.end)}return i+=e.slice(d),{text:i,secrets:l,spans:c}}function li(e,t){let n=[],r=0,o=a(s=>{let i=_o(s);for(let d=0;d<i;d++)n.push({kind:"literal",value:X})},"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 oe(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function xf(e){return oe(e)&&typeof e.id=="string"&&typeof e.type=="string"}function ci(e,t){let n=new ai(t,e.id),r=e[kr];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!==kr&&(cd.has(s)?o[s]=i:fd.has(s)&&Array.isArray(i)?o[s]=i.map(d=>ui(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[kr]=n.paths),o}function ui(e,t){return Array.isArray(e)?e.map(n=>ui(n,t)):xf(e)?ci(e,t):e}function yt(e,t=It()){let n={matcher:Rr(t),count:0};return{value:ui(e,n),count:n.count}}function pi(e,t=It()){return e.map(n=>{if(n.nodeName===void 0)return{...n};let r=yt({id:"conflict",type:"node",name:n.nodeName},t);return{...n,nodeName:r.value.name}})}function nt(e,t=It()){let n={...e,payload:{...e.payload,flow:yt(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=yt(e.baseFlow,t).value),e.rebase&&(n.rebase={...e.rebase,conflicts:pi(e.rebase.conflicts,t)}),n}function Lt(e,t=It){if(hd(e))return e;let n=t===It,r=n?dd.get(e):void 0;if(r)return r;let o={kind:e.kind,[fi]:e,async getFlows(){let s=await e.getFlows();return{rev:s.rev,flows:yt(s.flows,t()).value}},async getFlow(s){let i=await e.getFlow(s);return{rev:i.rev,flow:yt(i.flow,t()).value}},updateFlow:a(s=>e.updateFlow(s),"updateFlow")};return n&&dd.set(e,o),o}function hd(e){return fi in e}function we(e){return hd(e)?e[fi]:e}function rt(e,t){return e.accessLink||e.auth?Lt(t):t}function _o(e){let t=0,n=e.indexOf(X);for(;n!==-1;)t++,n=e.indexOf(X,n+X.length);return t}function xr(e){let t=e.split(X),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 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 {
9
+ `);n.push({before:i>=0?o.slice(i+1):o,after:d>=0?s.slice(0,d):s})}return n}function Po(e){return JSON.stringify([e.before,e.after])}function Ro(e){let t=new Map;return xr(e.text).forEach((n,r)=>{let o=Po(n),s=t.get(o)??[];s.push(r),t.set(o,s)}),t}function md(e,t,n,r){if(e===t)return{text:n,restored:r.filter(f=>f.kind==="secret").length};let o=xr(e),s=xr(t);if(o.length>s.length)return{error:`contains ${o.length} [REDACTED] placeholders but the original had ${s.length}`};let i=Ro({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(X),u=c[0]??"",p=0;for(let f=0;f<o.length;f++){let h=o[f];if(!h)break;let m=Po(h),g=i.get(m)??[];if(d.has(m))return{error:"ambiguous literal and secret [REDACTED] occurrences"};let w=l.get(m)??0,v=g[w]??-1;if(v<0)return{error:"a [REDACTED] placeholder is not on an unchanged original line"};l.set(m,w+1);let k=r[v];k?.kind==="secret"&&p++,u+=(k?.value??X)+(c[f+1]??"")}return{text:u,restored:p}}function Xt(e){if(oe(e))for(let t of Sf){let n=e[t];if(typeof n=="string"&&n.length>0)return`${t}=${n}`}}function Tr(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=>!oe(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 hi(e,t,n,r=[]){let o=a(c=>{let u=new Map;return ci(c,{matcher:Rr(It()),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(N=>N.kind==="secret"))return u;let w=i.get(`${t.id}\0${h}`),v=d.get(`${n.id}\0${m}`),k=xr(g.text),$=Ro(g),C=w?Ro(w):new Map,O=v?Ro(v):new Map,q=w?.occurrences.map(N=>N.value)??[],T=w?xr(w.text):[],S=w?.occurrences.some((N,P)=>{let A=T[P];return N.kind==="secret"&&A&&!$.has(Po(A))}),F=!1;return g.occurrences.forEach((N,P)=>{if(N.kind!=="secret")return;let A=k[P],I=A?Po(A):"",b=$.get(I)??[],M=C.get(I)??[],H=O.get(I)??[];if(b.length>1&&M.length>0&&M.length<b.length&&JSON.stringify(b.map(qt=>g.occurrences[qt]?.value))!==JSON.stringify(H.map(qt=>v?.occurrences[qt]?.value)))throw new Sr;let L=a((qt,hu)=>{let da=qt.get(I)??[],[Ss]=da;return da.length===1&&Ss!==void 0&&hu?.occurrences[Ss]?.kind==="secret"?Ss:void 0},"matches"),ee=L($,g),Se=L(C,w),G=L(O,v);if(!C.has(I)&&!S)return;if(ee===void 0||Se===void 0||G===void 0){F=!0;return}let Ve=v?.occurrences[G];w?.occurrences[Se]?.value===N.value&&Ve&&(q[Se]=Ve.value)}),F&&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."}),!w||F?u:w.text.split(X).reduce((N,P,A)=>N+(A?q[A-1]??X:"")+P,"")}if(Array.isArray(u)){let g=Array.isArray(c)?c:[],w=Array.isArray(p)?p:[],v=new Set,k=new Set;return u.map(($,C)=>{let O=Xt($),q=a((F,N)=>{let P=O===void 0?C:F.findIndex((A,I)=>!N.has(I)&&Xt(A)===O);return N.add(P),P},"match"),T=q(g,v),S=q(w,k);return l(g[T],$,w[S],`${f}[${T}]`,`${h}[${C}]`,`${m}[${S}]`)})}if(oe(u)){let g=oe(c)?c:{},w=oe(p)?p:{},v=a((k,$)=>k?`${k}.${$}`:$,"path");return Object.fromEntries(Object.entries(u).map(([k,$])=>[k,l(g[k],$,w[k],v(f,k),v(h,k),v(m,k))]).filter(([,k])=>k!==void 0))}return u},"walk");return l(e,t,n,"")}function gd(e,t){let n=li(t,[]);return"error"in md(e,t,t,n)}function To(e,t,n,r,o,s){if(typeof e=="string"){if(!e.includes(X))return e;let i=s.fields.get(`${r}\0${o}`);if(typeof t=="string"&&typeof n=="string"&&(i||n.includes(X))){let d=md(e,t,n,i?.occurrences??li(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=oe(c)&&c.redacted===!0?di(c,"redacted"):c,p=Xt(u),f=-1;if(p!==void 0){let h=d.map((m,g)=>Xt(m)===p&&!l.has(g)?g:-1).filter(m=>m>=0);h.length>0&&(f=h[0]??-1)}return f>=0?(l.add(f),To(u,i[f],d[f],r,`${o}[${f}]`,s)):To(u,void 0,void 0,r,`${o}[?]`,s)})}if(oe(e)){let i=oe(t)?t:{},d=oe(n)?n:{},l={};for(let[c,u]of Object.entries(e))l[c]=To(u,i[c],d[c],r,o?`${o}.${c}`:c,s);return l}return e}function di(e,t){let n={...e};return delete n[t],n}function Rf(e){let t=di(e,kr);return Array.isArray(t.env)&&(t.env=t.env.map(n=>!oe(n)||n.redacted!==!0?n:di(n,"redacted"))),t}function yd(e,t,n,r){let o=typeof e.id=="string"?e.id:"",s=o?t.get(o):void 0,i={},d=s?ci({...s,id:o},r):void 0;for(let[l,c]of Object.entries(e))if(l!==kr){if(fd.has(l)&&Array.isArray(c)){i[l]=c.map(u=>oe(u)?yd(u,t,n,r):u);continue}if(cd.has(l)){i[l]=c;continue}i[l]=To(c,d?.[l],s?.[l],o,l,n)}return Rf(i)}function wd(e,t,n=It()){let r=new Map;for(let l of t)oe(l)&&typeof l.id=="string"&&r.set(l.id,l);let o=new Map,s={matcher:Rr(n),count:0,fields:o},i={fields:o,restored:0,restoredFields:[],unresolved:[]};return{flow:yd(e,r,i,s),restored:i.restored,restoredFields:i.restoredFields,unresolved:i.unresolved}}function Cf(e){return typeof e.type=="string"&&Pf.has(e.type)?!0:_f.some(t=>typeof e[t]=="string"&&e[t]!=="")}function ld(e){let t=new Set,n=a((r,o)=>{if(!(o>12)){if(typeof r=="string"){if(r.length>pd)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(oe(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"&&Ef.has(r[c]))&&typeof i=="string"&&t.add(i),n(i,o+1)}},"walk");return n(e,0),t.delete(""),t}function Ln(e,t){return JSON.stringify(e)===JSON.stringify(t)}function vd(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]);for(let r of n)if(!(bd.has(r)||r==="nodes"||r==="configs")&&!Ln(e[r],t[r]))return!0;return!1}function If(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 Lf(e,t){return e.filter(n=>n.nodeId===t).map(n=>n.property).join(", ")}function Nf(e,t,n,r){let o=[],s=Tf.filter(d=>!Ln(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=[...ud].filter(d=>!Ln(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(",")}),Ln(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 Ff(e,t,n,r,o,s){let i=[];for(let d of If(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?!Ln(l?.env,u?.env):l!==void 0&&u!==void 0&&vd(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 Of(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&&!vd(c,l))continue;e.includes(d)||i.push(d);let u=typeof l.type=="string"?l.type:"node";Cf(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(h=>!bd.has(h)&&h!=="nodes"&&h!=="configs"&&!Ln(c?.[h],l[h])).sort().join(",")});let p=c?ld(c):new Set,f=[...ld(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 $f(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)oe(s)&&typeof s.id=="string"&&n.set(s.id,s)}return n}function kd(e,t,n,r=It()){let o=[...new Set(e.map(u=>u.nodeId))],s=String(t.id),i=new Map;for(let u of n)oe(u)&&typeof u.id=="string"&&i.set(u.id,u);let d=$f(t,s),l=[],c=new Set;for(let u of o){let p=i.get(u),f=d.get(u);!p||!f||(l.push(...Nf(u,Lf(e,u),p,f)),l.push(...Ff(u,p,i,d,s,c)))}return l.push(...Of(o,i,d,s,Rr(r))),l}var kr,cf,uf,ii,cd,pf,ud,pd,fd,od,sd,So,id,mf,ad,ai,fi,dd,Sf,Sr,Tf,Pf,_f,Ef,bd,Ne=W(()=>{"use strict";y();vn();Te();vn();kr="_aiRedactions",cf=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"]),uf=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"]),ii=new Set(["env","msg","flow","global","jsonata","bool","date","node","cred"]),cd=new Set(["id","type","z","g","x","y","w","h","wires","inputs","outputs","links","scope","icon","color","style","d","l","in","out","rev"]),pf=new Set(["name","label","info"]),ud=new Set(["func","initialize","finalize"]),pd=256*1024,fd=new Set(["nodes","configs","subflows"]),od=["name","p","keyValue","key","property","k"],sd={value:"type",v:"vt",to:"tot",valueValue:"valueType"},So=new Map;a(Rr,"createSecretNameMatcher");a(It,"currentSecretNamePatterns");a(Ar,"isSecretLikeValue");id=new WeakMap;a(ff,"contentRules");a(hf,"buildContentRules");mf=["PASSWORD","PASSWD","PASSPHRASE","SECRET","CLIENT_SECRET","TOKEN","API_KEY","APIKEY","PRIVATE","CREDENTIAL","PWD","PW"];a(gf,"shannonEntropy");a(yf,"looksRandom");a(wf,"unquotedRule");a(bf,"decodeURIComponentSafe");a(vf,"parseConnSegment");a(kf,"connectionStringSpans");ad=new WeakMap;a(Af,"scanTextForSecrets");a(li,"classifyPlaceholders");a(oe,"isPlainObject");a(xf,"isFlowEntity");ai=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&&Ar(t)||t.length>pd)return this.record(n,[t]),this.ctx.fields?.set(`${this.entityId}\0${n}`,{text:X,occurrences:[{kind:"secret",value:t}]}),X;let o=n.split(/[.[]/,1)[0]??"",s=Af(t,this.ctx.matcher,{code:ud.has(o)});return this.ctx.fields&&s.text.includes(X)&&this.ctx.fields.set(`${this.entityId}\0${n}`,{text:s.text,occurrences:li(t,s.spans)}),s.secrets.length>0?(this.record(n,s.secrets),s.text):t}env(t,n){return t.map((r,o)=>{if(!oe(r))return r;let s=typeof r.type=="string"?r.type:"";if(s==="cred"||typeof r.value!="string"||ii.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(pf.has(r))return this.string(t,n,!1);let s=o?.[`${r}Type`],i=typeof s=="string"&&ii.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)):oe(t)?this.object(t,n):t}object(t,n){let r=!1;for(let s of od){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 sd&&typeof i=="string"){let l=sd[s],c=l?t[l]:void 0,u=typeof c=="string"&&ii.has(c);o[s]=this.string(i,d,!u);continue}if(od.includes(s)&&typeof i=="string"){o[s]=this.string(i,d,!1);continue}o[s]=this.value(i,d,s,t)}return o}};a(ci,"redactEntity");a(ui,"redactValueInner");a(yt,"redactFlowData");a(pi,"redactConflictLabels");a(nt,"redactProposalForAi");fi=Symbol.for("ai-access.rawFlowAdapter"),dd=new WeakMap;a(Lt,"aiRedactingAdapter");a(hd,"isRedactingAdapter");a(we,"rawFlowAdapter");a(rt,"adapterForRequest");a(_o,"countRedactionPlaceholders");a(xr,"placeholderContexts");a(Po,"contextKey");a(Ro,"indexOccurrences");a(md,"restoreString");Sf=["name","keyValue","p","property","key","k"];a(Xt,"stableElementKey");a(Tr,"duplicateEnvWarnings");Sr=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(hi,"preserveLiveSecrets");a(gd,"hasNewLiteralPlaceholder");a(To,"restoreWalk");a(di,"withoutKey");a(Rf,"stripMarkers");a(yd,"restoreEntity");a(wd,"restoreRedactedPlaceholders");Tf=["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"]),_f=["url","host","hostname","server","broker","endpoint","uri"];a(Cf,"isOutboundNode");Ef=new Set(["env","global","flow"]);a(ld,"referencedNames");bd=new Set(["x","y","w","h","l"]);a(Ln,"sameJson");a(vd,"nodeChanged");a(If,"scopeIds");a(Lf,"restoredFieldList");a(Nf,"secretNodeWarnings");a(Ff,"scopeWarnings");a(Of,"tabWarnings");a($f,"proposedNodeMap");a(kd,"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 jf(e){return(0,Ad.createHash)("sha256").update(Qt(e)).digest("hex").slice(0,16)}function ke(e){return Array.isArray(e)?e.filter(t=>t&&typeof t=="object"&&typeof t.id=="string"):[]}function Fn(e){let t=new Map;for(let n of ke(e.nodes))t.set(n.id,{node:n,config:!1});for(let n of ke(e.configs))t.set(n.id,{node:n,config:!0});return t}function ot(e){let t={};for(let n of wi)e[n]!==void 0&&(t[n]=e[n]);return t}function Co(e,t){let n=new Set([...Object.keys(e??{}),...Object.keys(t??{})]),r=[];for(let o of n)Sd.has(o)||Qt(e?.[o])!==Qt(t?.[o])&&r.push(o);return r.sort()}function mi(e){if(e===void 0)return new Map;if(!Array.isArray(e))return;let t=new Map;for(let n of e){if(!Nn(n)||typeof n.name!="string"||!n.name||t.has(n.name))return;t.set(n.name,n)}return t}function Nn(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function Rd(e,t){let n=[];return Pr(e,t,e,void 0,n),n.sort()}function Pr(e,t,n,r,o){let s=[],i=a((f,h)=>Qt(f)===Qt(h),"same"),d=new Set([e.type,t.type,n.type].includes("function")?Mf:[]);(!i(e.outputs,t.outputs)||!i(e.outputs,n.outputs))&&(d.add("outputs"),d.add("wires")),[e.type,t.type,n.type].includes("switch")&&(d.add("rules"),d.add("outputs"),d.add("wires"));let l=a(f=>[...d].some(h=>!i(e[h],f[h])),"changedInGroup"),c=l(t)&&l(n),u=a((f,h,m,g)=>{if(i(f,h))return m;if(g==="env"){let w=mi(f),v=mi(h),k=mi(m);if(w&&v&&k){let $=[];for(let C of new Set([...v.keys(),...k.keys(),...w.keys()])){let O=u(w.get(C),v.get(C),k.get(C),`env[${JSON.stringify(C)}]`);O!==void 0&&$.push(O)}return $}}if((g==="credentials"||g.startsWith("credentials.")||g.startsWith("env["))&&Nn(h)&&(Nn(m)||m===void 0&&f===void 0)&&(f===void 0||Nn(f))){let w=Nn(f)?f:{};return p(w,h,Nn(m)?m:{},g)}return o?.push(g),i(f,m)||Df.has(g)?h:(s.push(g),r==="live"?m:h)},"merge"),p=a((f,h,m,g)=>{let w=[];for(let v of new Set([...Object.keys(f),...Object.keys(h),...Object.keys(m)])){if(!g&&Sd.has(v))continue;let k=g?`${g}.${v}`:v,$;!g&&c&&d.has(v)?((!i(f[v],h[v])||!i(f[v],m[v]))&&s.push(v),i(f[v],h[v])||o?.push(v),$=r==="live"?m[v]:h[v]):$=u(f[v],h[v],m[v],k),$!==void 0&&w.push([v,$])}return Object.fromEntries(w)},"mergeObject");return{value:p(e,t,n,""),conflicts:s.sort()}}function Nt(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 Td(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 st(e,t,n){let r=Td(e,t);if(!r)return;let o=new Set(ke(n?.configs).map(c=>c.id)),s=new Set(ke(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,...ot(r.tab),nodes:i};return d.length>0&&(l.configs=d),l}function Eo(e,t){if(typeof e=="string")return e.includes(X)&&(typeof t!="string"||gd(e,t));if(Array.isArray(e)){let n=Array.isArray(t)?t:[],r=new Set;return e.some((o,s)=>{let i=Xt(o),d=i===void 0?s:n.findIndex((l,c)=>!r.has(c)&&Xt(l)===i);return r.add(d),Eo(o,n[d])})}if(e&&typeof e=="object"){let n=t&&typeof t=="object"?t:{};return Object.entries(e).some(([r,o])=>Eo(o,n[r]))}return!1}function Pd(e){let{flowId:t,proposed:n,liveFlows:r,liveRev:o}=e,s=[],i=a(b=>{s.length<Hf&&s.push(b)},"push"),d=Td(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?st(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=st(r,t,n),u=Fn(l),p=Fn(c),f=Fn(n),h=[],m=ot(l),g=ot(d.tab),w=ot(n),v=Co(m,g),k=Pr(m,w,g),$=k.value;v.length>0&&h.push(t),k.conflicts.length>0&&i({kind:"tab_changed_both",nodeId:t,fields:k.conflicts,message:"Both the editor and the proposal changed the same flow tab fields.",choices:["live","proposed"]}),$={...$};for(let b of wi)g[b]!==void 0&&$[b]===void 0&&($[b]=b==="env"?[]:xd[b]);let C=new Map,O=new Set([...u.keys(),...p.keys(),...f.keys()]),q=new Map;for(let b of r)b.id!==t&&b.z!==t&&q.set(b.id,b);for(let b of O){let M=u.get(b),H=p.get(b),L=f.get(b),ee=M&&H?Co(M.node,H.node):[],Se=M&&L?Co(M.node,L.node):[];if((M&&(!H||ee.length>0)||!M&&H)&&h.push(b),L&&M&&q.has(b))i({kind:"id_collision",nodeId:b,...Nt(L.node),message:"This node id now exists outside this flow tab.",choices:["live"]});else if(M&&H&&L){let G=Pr(M.node,L.node,H.node);G.conflicts.length===0?C.set(b,{...L,node:G.value}):i({kind:"node_changed_both",nodeId:b,...Nt(H.node),fields:G.conflicts,message:"Both the editor and the proposal changed the same node fields.",choices:["live","proposed"]})}else M&&H&&!L?ee.length===0?C.set(b,null):i({kind:"node_deleted_in_proposal_modified_live",nodeId:b,...Nt(H.node),fields:ee,message:"The proposal deletes this node, but it was changed in the editor.",choices:["live","proposed"]}):M&&!H&&L?Se.length===0?C.set(b,null):i({kind:"node_deleted_live",nodeId:b,...Nt(L.node),fields:Se,message:"This node was deleted in the editor, but the proposal changes it.",choices:["live","proposed"]}):M&&!H&&!L?C.set(b,null):!M&&H&&L?i({kind:"id_collision",nodeId:b,...Nt(L.node),fields:Co(H.node,L.node),message:"The proposal adds a node whose id was also added in the editor.",choices:["live","proposed"]}):!M&&H&&!L?C.set(b,H):!M&&!H&&L&&(q.get(b)??(b===t?d.tab:void 0)?i({kind:"id_collision",nodeId:b,...Nt(L.node),message:"The proposal adds a node whose id now exists on another flow tab.",choices:["live"]}):C.set(b,L))}if(s.length>0)return{state:"conflict",rev:o,conflicts:s,liveChangedIds:gi(h)};let T=[],S=[],F=new Set,N=a(b=>{if(F.has(b))return;let M=C.get(b);F.add(b),M&&(M.config?S:T).push(_d(M.node))},"emit");for(let b of ke(n.nodes))N(b.id);for(let b of ke(n.configs))N(b.id);for(let b of ke(c.nodes))N(b.id);for(let b of ke(c.configs))N(b.id);let P={id:t,type:d.tab.type,...$,nodes:T};(S.length>0||Array.isArray(n.configs))&&(P.configs=S);let A=new Set([...T,...S].map(b=>b.id)),I=new Set(r.filter(b=>b.id!==t&&b.z!==t).map(b=>b.id));for(let b of[...T,...S]){let M=[];if(Array.isArray(b.wires)){for(let L of b.wires)if(Array.isArray(L))for(let ee of L)typeof ee=="string"&&!A.has(ee)&&M.push(ee)}if(typeof b.g=="string"&&b.g.length>0&&!A.has(b.g)&&M.push(b.g),b.type==="group"&&Array.isArray(b.nodes))for(let L of b.nodes)typeof L=="string"&&!A.has(L)&&M.push(L);if(Array.isArray(b.links))for(let L of b.links)typeof L=="string"&&!A.has(L)&&!I.has(L)&&M.push(L);let H=[...new Set(M)].sort();H.length>0&&i({kind:"dangling_reference",nodeId:b.id,...Nt(b),fields:H,message:"After merging, this node points to a node that no longer exists (deleted in the editor).",choices:["live"]}),Eo(b,p.get(b.id)?.node)&&i({kind:"redacted_placeholder",nodeId:b.id,...Nt(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 M of[d.tab.in,d.tab.out])if(Array.isArray(M)){for(let H of M)if(Array.isArray(H?.wires))for(let L of H.wires)typeof L?.id=="string"&&!A.has(L.id)&&b.add(L.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 Eo($,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:gi(h)}:Uf(P,n,o,[],gi(h))}function Uf(e,t,n,r,o){let s=wt(e);return{state:s===wt(t)?"clean":"merged",rev:n,conflicts:r,merged:e,mergedHash:s,liveChangedIds:o}}function wt(e){return jf(Bf(e))}function Bf(e){let t=[...ke(e.nodes),...ke(e.configs)].map(_d).sort((n,r)=>n.id<r.id?-1:n.id>r.id?1:0);return{...xd,...ot(e),nodes:t}}function _d(e){if(!("z"in e))return e;let t={...e};return delete t.z,t}function gi(e){return[...new Set(e)]}function Cd(e){let{flowId:t,nodeId:n,choice:r,liveFlows:o}=e,s=[],i=st(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:Ft(i),proposed:Ft(e.proposed),warnings:s}}let d=Ft(e.base),l=Ft(e.proposed);if(r==="proposed"&&(s.push(...Tr(d,l).filter(h=>h.nodeId===n)),s.push(...Tr(i,l).filter(h=>h.nodeId===n&&!s.some(m=>m.nodeId===h.nodeId)))),n===t){let h=ot(i),m=r==="proposed"?hi({id:t,type:i.type??"tab",...ot(d)},{id:t,type:i.type??"tab",...ot(l)},{id:t,type:i.type??"tab",...h},s):ot(l),g=Pr(ot(d),m,h,r).value;for(let w of wi)delete d[w],h[w]!==void 0&&(d[w]=Ft(h[w])),delete l[w],g[w]!==void 0&&(l[w]=Ft(g[w]));return{base:d,proposed:l,warnings:s}}let u=Fn(i).get(n),p=Fn(d).get(n),f=Fn(l).get(n);if(p&&f&&u){let h=r==="proposed"?hi(p.node,f.node,u.node,s):f.node;yi(l,n,{...f,node:Pr(p.node,h,u.node,r).value})}else r==="live"&&yi(l,n,u?{...u,node:Ft(u.node)}:void 0);return yi(d,n,u?{...u,node:Ft(u.node)}:void 0),{base:d,proposed:l,warnings:s}}function yi(e,t,n){let r=ke(e.nodes),o=ke(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=[...ke(e.configs),n.node]:e.nodes=[...e.nodes,n.node]}function Ft(e){return e===void 0?e:JSON.parse(JSON.stringify(e))}var Ad,wi,Mf,Df,xd,Sd,Hf,On=W(()=>{"use strict";y();Ad=require("crypto");vn();Ne();wi=["label","disabled","info","env"],Mf=["func","libs","initialize","finalize","outputs"],Df=new Set(["x","y"]),xd={label:"",disabled:!1,info:"",env:[]},Sd=new Set(["z"]),Hf=200;a(Qt,"stableStringify");a(jf,"hashTab");a(ke,"asNodes");a(Fn,"mapTab");a(ot,"tabProps");a(Co,"changedFields");a(mi,"envMap");a(Nn,"isObject");a(Rd,"changedFieldPaths");a(Pr,"mergeFields");a(Nt,"nodeLabel");a(Td,"liveTabFromFlows");a(st,"nestedLiveTab");a(Eo,"containsNewPlaceholder");a(Pd,"rebaseFlowTab");a(Uf,"finish");a(wt,"hashFlowTab");a(Bf,"normalizeForHash");a(_d,"withoutZ");a(gi,"dedupe");a(Cd,"applyConflictChoice");a(yi,"setNode");a(Ft,"clone")});function Ed(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 Id(e,t){return Rd(e,t).filter(n=>!qf.has(n))}function Ld(e){let t=Ed(e.baseFlow??{}),n=Ed(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=Id(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=Id(o,s);return i.length>0&&r.set(e.payload.flowId,new Set(i)),r}function Wf(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 it(e,t,n){let r=Ld(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=si(s),d=n?mt(s.createdBy)===n:mt(s.createdBy)===mt(t.createdBy),l=Wf(r,Ld(s));if(l.nodeIds.length===0)continue;let c={id:s.id,title:i,displayName:oi(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 at(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 me(e){return e>0?{autoClosedCount:e}:{}}var qf,en=W(()=>{"use strict";y();tt();qe();On();qf=new Set(["x","y","id","z"]);a(Ed,"collectNodes");a(Id,"changedFieldKeys");a(Ld,"computeProposalTouches");a(Wf,"overlapBetween");a(it,"findConflictingProposals");a(at,"conflictWarningFields");a(me,"autoClosedFields")});function $d(e,t){Od=e,Io=t??(()=>Date.now())}function Md(){let e=Od();return(Number.isFinite(e)&&e>=1?e:Nd)*Fd}function zf(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 Lo(e,t=Io()){let n=Date.parse(e.createdAt);return Number.isFinite(n)&&t-n>Md()}function se(e,t={}){let n=Io(),r=Math.round(Md()/Fd),o=0;for(let s of e.list())if(U(s.status)&&!(t.flowId&&s.payload.flowId!==t.flowId)&&!e.isApproving(s.id)&&Lo(s,n))try{e.markClosed(s.id,"expired",zf(r)),o+=1}catch{}return o}function bi(e,t){let n=Pd({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 vi(e,t=Io()){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 We(e,t,n,r={}){let o=bi(t,n);return{proposal:e.isApproving(t.id)&&!r.duringApprove?e.get(t.id):e.recordRebase(t.id,vi(o)),result:o}}async function _r(e,t,n={}){let r=se(e,n.flowId?{flowId:n.flowId}:{}),o=n.live;if(!o&&t)try{o=await we(t).getFlows()}catch{}if(!o)return{expired:r,conflicts:0,checked:0};let s=ze(e,o,n);return{...s,expired:r+s.expired}}function ze(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(Lo(o)){r.expired+=se(e,{flowId:o.payload.flowId});continue}let{proposal:s}=We(e,o,t);r.checked+=1,s.status==="conflict"&&(r.conflicts+=1)}catch{}return r}function Dd(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 dt(e){if(e)try{return await we(e).getFlows()}catch{return}}var Nd,Fd,Od,Io,lt=W(()=>{"use strict";y();tt();On();Ne();Nd=7,Fd=864e5,Od=a(()=>Nd,"ttlDaysProvider"),Io=a(()=>Date.now(),"nowProvider");a($d,"configureProposalTtl");a(Md,"proposalTtlMs");a(zf,"ttlExpiredReason");a(Lo,"isPastTtl");a(se,"expireByTtl");a(bi,"rebaseProposal");a(vi,"rebaseInfoFrom");a(We,"syncOneProposal");a(_r,"syncOpenProposals");a(ze,"rebaseOpenProposalsWith");a(Dd,"createDebouncedSync");a(dt,"tryRawLive")});var Gd={};la(Gd,{applyFlowNodeOps:()=>tn,applyReplaceTextToValue:()=>zd,buildPatchedFlowTab:()=>Cr,collectSetProps:()=>ki,decodeBase64UrlJson:()=>Er,findTextOccurrences:()=>Wd,interpretPatch:()=>Ir});function qd(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function Gf(e){if(typeof e!="object"||e===null||Array.isArray(e))throw new x(`add op needs node as an object (got ${qd(e)})`,"invalid_node")}async function Cr(e,t,n){return tn(e,t,n.map(r=>({op:"set",nodeId:r.nodeId,set:r.set})))}function Wd(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 zd(e,t,n,r,o){if(Bd.has(t))throw new x(`Cannot change protected node property '${t}' via replace_text`,"protected_property");if(typeof n!="string"||n.length===0)throw new x("find must be a non-empty string","missing_find");if(typeof r!="string")throw new x("replace must be a string","invalid_replace");if(e===void 0)throw new x(`Field '${t}' was not found on the node`,"field_not_found");if(typeof e!="string")throw new x(`Field '${t}' is not a string (got ${e===null?"null":typeof e})`,"field_not_string");let s=Wd(e,n);if(s.length===0)throw new x(`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 x(`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 x(`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 x(`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 Kf(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 ${qd(r)})`});continue}let o=r;(!o.id||typeof o.id!="string")&&t.push({code:"missing_node_id",error:"add op needs node.id"});try{Ct(o.type,"node.type")}catch(s){s instanceof x&&t.push({code:s.code,error:s.message,nodeId:o.id})}"wires"in o&&Cn(o.wires,t,typeof o.id=="string"?o.id:void 0)}else n.op==="set"&&typeof n.set=="object"&&"wires"in n.set&&Cn(n.set.wires,t,n.nodeId);return t}function Jf(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 tn(e,t,n,r={}){if(!t)throw new x("flow id is required","missing_flow_id");if(!n.length)throw new x("At least one node edit is required","missing_edits");let o=Kf(n);o.push(...Ys([n.flatMap(m=>m.op==="add"?[m.node]:[])],"the batch (add ops)")),gt(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 Va(r.nodeTypes,m.node.type),jd(c,t,m)}catch(g){if(g instanceof x)u.push({code:g.code,error:g.message,...g.statusCode!==400?{statusCode:g.statusCode}:{},...g.details??{}});else throw g}u.push(...Xs(c,t));let p=Jf(n),f=await Zs(c,r.nodeTypes,{softWarningNodeIds:p});u.push(...f.errors),gt(u);for(let m of n)jd(i,t,m);let h=[...f.warnings];if(r.nodeTypes){let m=await Qs(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 Hd(e,t,n){if(!t||typeof t!="string")throw new x("Each edit needs a node id","missing_node_id");let r=e.findIndex(o=>o.id===t);if(r<0||!e[r])throw new x(`Node '${t}' was not found in flow '${n}'`,"node_not_found",{statusCode:404});return r}function jd(e,t,n){if(n.op==="add"){let i=n.node;if(!i.id||typeof i.id!="string")throw new x("add op needs node.id","missing_node_id");if(Ct(i.type,"node.type"),e.some(d=>d.id===i.id))throw new x(`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=Hd(e,n.nodeId,t);e.splice(i,1);return}let r=Hd(e,n.nodeId,t),o=e[r];if(!o)throw new x(`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 x(`Field '${n.field}' was not found on the node`,"field_not_found");let i=zd(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(Bd.has(i))throw new x(`Cannot change protected node property '${i}' via GET propose`,"protected_property");if(i==="wires"){let l=[];Cn(d,l,n.nodeId),gt(l)}s[i]=d}e[r]=s}function Ud(e){if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean"||typeof e=="bigint")return String(e)}function ki(e){let t={},n=e.set;if(n&&typeof n=="object"&&!Array.isArray(n))for(let[r,o]of Object.entries(n)){let s=Ud(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=Ud(o);i!==void 0&&(t[s]=i)}}return t}function Er(e){let t;try{t=Buffer.from(e,"base64url").toString("utf8")}catch{throw new x("patch must be base64url-encoded JSON","invalid_patch")}try{return JSON.parse(t)}catch{throw new x("patch JSON is malformed","invalid_patch_json")}}function Vf(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 Yf(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 x("occurrence must be a positive integer","invalid_occurrence");if(!Number.isFinite(t))throw new x("occurrence must be a positive integer","invalid_occurrence");return Math.trunc(t)}function Zf(e){let t=e.node;if(t!==void 0)return Gf(t),{op:"add",node:t};let n={...e};return delete n.op,{op:"add",node:n}}function Xf(e){let t=Ai(e);if(!t)throw new x("replace_text op needs node id","missing_node_id");let n=typeof e.field=="string"?e.field:"";if(!n)throw new x("replace_text op needs field","missing_field");if(typeof e.find!="string")throw new x("replace_text op needs find string","missing_find");if(typeof e.replace!="string")throw new x("replace_text op needs replace string","invalid_replace");let r=Yf(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 Qf(e){let t=Ai(e);if(!t)throw new x("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 eh(e){let t=typeof e.op=="string"?e.op.toLowerCase():"set";if(t==="add")return Zf(e);if(t==="remove"){let n=Ai(e);if(!n)throw new x("remove op needs id","missing_node_id");return{op:"remove",nodeId:n}}if(t==="replace_text")return Xf(e);if(t!=="set")throw new x("nodes[].op must be 'add', 'remove', 'set', or 'replace_text'","invalid_patch");return Qf(e)}function Ir(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new x("patch must be a JSON object","invalid_patch");let n=e;if(n.type==="replace_flow"||n.flow&&typeof n.flow=="object"){let r=Vf(n,t);if(!r)throw new x("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 x("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 x("Each patch.nodes entry must be an object","invalid_patch");o.push(eh(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 x("patch must include nodes[] edits or type/flow for replace_flow","invalid_patch")}var Bd,No=W(()=>{"use strict";y();le();ei();Zt();Bd=new Set(["id","type","z"]);a(qd,"describeNonObjectNode");a(Gf,"assertAddNodeObject");a(Cr,"buildPatchedFlowTab");a(Wd,"findTextOccurrences");a(zd,"applyReplaceTextToValue");a(Kf,"collectOpShapeIssues");a(Jf,"touchedNodeIdsFromOps");a(tn,"applyFlowNodeOps");a(Hd,"requireNodeIndex");a(jd,"applyOneNodeOp");a(Ud,"stringifyQueryValue");a(ki,"collectSetProps");a(Er,"decodeBase64UrlJson");a(Vf,"resolveReplaceFlowId");a(Ai,"resolveNodeId");a(Yf,"parseOccurrence");a(Zf,"parseAddOp");a(Xf,"parseReplaceTextOp");a(Qf,"parseSetOp");a(eh,"parseNodeOp");a(Ir,"interpretPatch")});function nn(e,t){return mt(e.createdBy)===t.id}function Si(e,t){return!t||t.length===0||t.includes(e)}function th(e){return typeof e=="string"&&Jd.has(e)}function Mn(e,t){let n=nh[e],r=e!=="status"&&Array.isArray(t);return new x(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 Fo(e,t){if(Array.isArray(t))throw Mn(e,t);return t}function rh(e){return typeof e=="number"?Number.isInteger(e)?e:null:typeof e=="string"&&/^-?\d+$/.test(e.trim())?Number(e.trim()):null}function Ri(e){return e==null||e===""}function oh(e){if(!Ri(e)){if(e==="true"||e==="1"||e===!0)return!0;if(e==="false"||e==="0"||e===!1)return!1;throw Mn("mine",e)}}function Ti(e){if(Ri(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(!th(o))throw Mn("status",e);n.includes(o)||n.push(o)}}return n.length>0?n:void 0}function Kd(e,t,n,r){if(Ri(t))return;let o=rh(t);if(o===null||o<n||o>r)throw Mn(e,t);return o}function Oo(e){let t={},n=oh(Fo("mine",e.mine));n!==void 0&&(t.mine=n);let r=Ti(e.status);r&&(t.status=r);let o=Fo("flowId",e.flowId??e.flow_id??e.flow);typeof o=="string"&&o.length>0&&(t.flowId=o);let s=Kd("limit",Fo("limit",e.limit),1,$n);s!==void 0&&(t.limit=s);let i=Kd("offset",Fo("offset",e.offset),0,Number.MAX_SAFE_INTEGER);return i!==void 0&&(t.offset=i),t}function rn(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 sh(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 Dn(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||!nn(c,t)).filter(c=>Si(c.status,r.status)).map(c=>({...ce(nt(c),n),mine:nn(c,t)})),l=sh(d,r);return{proposals:l.items,total:l.total,limit:l.limit,offset:l.offset}}function bt(e,t,n,r={}){if(!n)throw new x("Missing proposal id","missing_proposal_id");let o=e.get(n);return{...ce(nt(o),r),mine:nn(o,t)}}function ih(e){let t={kind:"access-link",linkId:e.id};return typeof e.label=="string"&&e.label&&(t.label=e.label),t}function Hn(e,t,n,r,o,s={}){if(!r)throw new x("Missing proposal id","missing_proposal_id");let i;try{i=e.get(r)}catch(d){throw d instanceof ye,d}if(!nn(i,n))throw new _t(r);if(!U(i.status))throw new Q(r,i.status);return{...Pi(e,t,ih(n),r,o,s),mine:!0}}function Pi(e,t,n,r,o,s={}){let i=e.get(r);if(n.kind==="access-link"&&mt(i.createdBy)!==n.linkId)throw new _t(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=ce(nt(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:mt(i.createdBy),...c.flowLabel?{flowLabel:c.flowLabel}:{}}),c}function ah(e,t,n,r={}){let o;try{o=e.get(t.id)}catch{return{}}if(r.live){ze(e,r.live,{flowId:o.payload.flowId});try{o=e.get(t.id)}catch{return{}}}return U(o.status)?at(it(e,o,n)):{}}function be(e,t,n,r={},o={}){let s="mine"in n?n:{...ce(n,r),mine:nn(n,t)},i=0;if(o.live)try{let c=e.get(s.id).payload.flowId;i=ze(e,o.live,{flowId:c}).expired;let u=e.get(s.id);s={...ce(u,r),mine:nn(u,t)}}catch{}let d=ue(nt(s),r),l={...d,mine:s.mine,...me(i)};return o.includeConflicts!==!1&&U(d.status)&&Object.assign(l,ah(e,d,t.id)),l}async function vt(e){let t=await e.getFlows();return{rev:t.rev,flows:t.flows}}async function Ge(e,t){if(!t)throw new x("Missing flow id","missing_flow_id");let n=await e.getFlow(t);return{rev:n.rev,flow:_n(t,n.flow)}}function dh(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 Vd(e,t){if("nodes"in e&&e.nodes!==void 0&&!Array.isArray(e.nodes))throw new x("payload.nodes must be an array of node ops","invalid_nodes");if(t!==void 0&&!Array.isArray(t))throw new x("nodes must be an array of node ops","invalid_nodes")}async function lh(e,t,n,r,o){let{applyFlowNodeOps:s,interpretPatch:i}=await Promise.resolve().then(()=>(No(),Gd));Vd(n,r);let d=Array.isArray(n.nodes)?n.nodes:Array.isArray(r)?r:null;if(!d)throw new x("payload.flow is required","missing_flow");let l=n.flowId,c=typeof l=="string"?l:"";if(!c)throw new x("payload.flowId is required","missing_flow_id");let u=i({nodes:d,flowId:c},c);if(u.kind!=="nodes")throw new x("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 re(t,p.expectedRev,"submit_proposal");return{flowId:u.flowId,flow:p.flow}}async function ch(e,t,n,r,o){let s=n.flowId,i=n.flow,d=!i||typeof i!="object"||Array.isArray(i);if(d&&Vd(n,r),d&&(Array.isArray(n.nodes)||Array.isArray(r))){let l=await lh(e,t,n,r,o);s=l.flowId,i=l.flow}if(typeof s!="string"||s.length===0)throw new x("payload.flowId is required","missing_flow_id");if(!i||typeof i!="object"||Array.isArray(i))throw new x("payload.flow is required","missing_flow");return{flowId:s,flow:i}}function uh(e,t){try{return{safeExpectedRev:Qe(e.slice(0,200),"expectedRev"),safeFlowId:Qe(t.slice(0,200),"flowId")}}catch{throw new x("expectedRev and payload.flowId must be safe path segments","invalid_path_segment")}}function ph(e,t){let n=wd(Ue(e),t);return n.unresolved.length>0&&gt(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 Yd(e,t,n){let r=e.type,o=dh(e),s=e.payload;if(r!=="replace_flow")throw new x("type must be 'replace_flow'","invalid_proposal_type");if(!o)throw new x("expectedRev is required (top-level field next to type and payload)","missing_expected_rev");if(!s||typeof s!="object"||Array.isArray(s))throw new x("payload must be an object","invalid_payload");let i=s,d=we(t),l=await d.getFlows(),c=yo(l,d.kind);if(l.rev!==o){let T=js(d)?.get(o);if(!T)throw new re(o,l.rev,"submit_proposal");c=yo(T,d.kind)}let u=await ch(Lt(c),o,i,e.nodes,n),{safeExpectedRev:p,safeFlowId:f}=uh(o,u.flowId),h=u.flow,m=Buffer.byteLength(JSON.stringify(s),"utf8");if(m>Y)throw new he("proposal payload",m,Y);gt(Xa(h,f));let w=await c.getFlows();if(w.rev!==p)throw new re(p,w.rev,"submit_proposal");let v=ph(h,w.flows),k=Ue(v.flow);gt(Za(k,f,w.flows));let $=await c.getFlow(f);if($.rev!==p)throw new re(p,$.rev,"submit_proposal");let C=Ue($.flow);n&&await Ja(n,k);let{errors:O,warnings:q}=await Qa(k,{flowId:f,baseFlow:C,...n?{nodeTypes:n}:{}});return gt(O),q.unshift(...Tr(C,k).map(T=>({...T})),...kd(v.restoredFields,k,w.flows).map(T=>({...T}))),{safeExpectedRev:p,safeFlowId:f,safeFlow:k,safeBaseFlow:C,restoration:v,flowWarnings:q,head:l}}function xi(e){let t={kind:"access-link",linkId:e.id};return e.label!==void 0&&(t.label=e.label),t}async function Ot(e,t,n,r,o,s={},i){let d=await Yd(r,o,i),{safeExpectedRev:l,safeFlowId:c,safeFlow:u,safeBaseFlow:p,restoration:f,flowWarnings:h}=d,m=xi(n),g=n.oauthClientId&&n.oauthUser?{kind:"oauth",clientId:n.oauthClientId,user:n.oauthUser}:m,w=vr(r.title)??ri(p,u),v=e.create({type:"replace_flow",expectedRev:l,payload:{flowId:c,flow:u},baseFlow:p,...f.restoredFields.length>0?{restoredSecrets:f.restoredFields}:{},title:w,createdBy:g,...h.length>0?{warnings:h}:{}}),k=We(e,v,d.head).proposal,$=ce(nt(k),s),C=$.flowLabel||void 0;return t.append("proposal.created",m,{proposalId:k.id,flowId:c,expectedRev:l,...d.head.rev!==l?{liveRev:d.head.rev}:{},...k.rebase?{rebaseState:k.rebase.state}:{},title:$.title,displayName:$.displayName,...C?{flowLabel:C}:{},...h.length>0?{warningCount:h.length}:{},...f.restored>0?{restoredRedactions:f.restored}:{}}),$}async function $o(e,t,n,r,o,s,i={},d){if(!r)throw new x("Missing proposal id","missing_proposal_id");let l=e.get(r);if(!nn(l,n))throw new _t(r);if(!U(l.status))throw new Q(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 x("Use nodeId + choice alone, or expectedRev + payload to replace the body; do not mix the modes.","invalid_choice");let m=await _i(e,t,xi(n),r,o.nodeId,o.choice,s);return{...ce(nt(m),i),mine:!0}}let c=await Yd({type:"replace_flow",...o,payload:o.payload??{flowId:l.payload.flowId}},s,d);if(c.safeFlowId!==l.payload.flowId)throw new x(`payload.flowId must stay '${l.payload.flowId}' (submit a new proposal for another flow)`,"flow_id_mismatch");let u=vr(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=We(e,p,c.head).proposal,h=ce(nt(f),i);return t.append("proposal.resolved",xi(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 _i(e,t,n,r,o,s,i){if(s!=="live"&&s!=="proposed")throw new x("choice must be 'live' or 'proposed'","invalid_choice");if(typeof o!="string"||o.length===0||o.length>200)throw new x("nodeId is required","missing_node_id");let d=await we(i).getFlows(),l=e.get(r);if(!U(l.status))throw new Q(r,l.status);let c=We(e,l,d).proposal,u=c.rebase?.conflicts.find(m=>m.nodeId===o);if(!u)throw new x("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 x("That choice is not available for this conflict.","invalid_choice");let p;try{p=Cd({flowId:c.payload.flowId,base:c.baseFlow,proposed:c.payload.flow,liveFlows:d.flows,nodeId:o,choice:s})}catch(m){throw m instanceof Sr?new et(r,c.rebase?.conflicts??[]):new x("This conflict cannot be resolved by hand.","invalid_choice")}let f=e.updateBody(r,{baseFlow:Ue(p.base),flow:Ue(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(m=>m.nodeId!==o),...n.kind!=="admin"&&s==="proposed"?[{nodeId:o,fields:u.fields?.length?u.fields:[""]}]:[]],warnings:[...(c.warnings??[]).filter(m=>m.code!=="secret_may_revert"||m.nodeId!==o),...p.warnings]}),h=We(e,f,d).proposal;return t.append("proposal.conflict_resolved",n,{proposalId:r,flowId:h.payload.flowId,nodeId:o,choice:s,kind:u.kind,chooser:n.kind==="admin"?"human":"ai",status:h.status}),h}async function Lr(e,t){let n=we(e),r;try{r=(await n.getFlows()).rev}catch{return e}if(r===t)return e;let o=js(n)?.get(t);if(!o)return e;let s=yo(o,n.kind);return e===n?s:Lt(s)}function J(e,t="rest"){return Nr(e,t)}var $n,Jd,nh,x,le=W(()=>{"use strict";y();Ee();mo();Us();tt();co();de();ct();ei();Zt();en();qe();On();lt();Ne();a(nn,"isMineProposal");$n=200,Jd=new Set(["pending","conflict","conflict","approved","rejected","failed","withdrawn","expired","superseded"]);a(Si,"matchesStatusFilter");a(th,"isProposalStatus");nh={status:[...Jd].join(", "),limit:`integer 1\u2013${$n}`,offset:"integer \u2265 0",mine:"true, false, 1, 0",flowId:"one flow tab id"};a(Mn,"invalidFilterError");a(Fo,"singleQueryValue");a(rh,"parseIntegerFilter");a(Ri,"isUnset");a(oh,"parseMineFilter");a(Ti,"parseStatusFilter");a(Kd,"parseRangeFilter");a(Oo,"parseListProposalsQuery");a(rn,"listPageFields");a(sh,"applyListPagination");a(Dn,"listAccessibleProposals");a(bt,"getAccessibleProposal");a(ih,"accessLinkActor");a(Hn,"withdrawMyProposal");a(Pi,"withdrawProposalAsActor");a(ah,"conflictsForProposal");a(be,"presentAccessibleProposalForApi");x=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(vt,"listFlows");a(Ge,"getFlow");a(dh,"resolveExpectedRev");a(Vd,"assertNodesArrayOrAbsent");a(lh,"materializePayloadFromNodes");a(ch,"resolveReplaceFlowPayload");a(uh,"assertSafeRevAndFlowId");a(ph,"strictRestoreOrThrow");a(Yd,"prepareReplaceFlowSubmission");a(xi,"auditActorFor");a(Ot,"submitReplaceFlowProposal");a($o,"resolveMyProposal");a(_i,"resolveConflictChoice");a(Lr,"adapterAtRev");a(J,"aiOpsErrorToHttp")});function Ci(e){return e==="get_link"?86400:2592e3}function Ei(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 on,Fr=W(()=>{"use strict";y();a(Ci,"maxTtlSecondsForKind");a(Ei,"ttlCapErrorMessage");on="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 _(e,t,n,r,o=ie,s="error"){return{code:e,status:t,meaning:n,hint:r,channels:o,kind:s}}function Or(e){return hh.get(e)?.hint}function Zd(e){return e.replace(/\\/g,"\\\\").replace(/\|/g,"\\|")}function Xd(){let e=a(s=>`| \`${s.code}\` | ${s.status} | ${Zd(s.meaning)} | ${Zd(s.hint)} | ${s.channels.join(", ")} |`,"row"),t=["| Code | HTTP | Meaning | Next step | Channels |","| --- | --- | --- | --- | --- |"],n=Mo.filter(s=>s.kind==="error"),r=Mo.filter(s=>s.kind==="warning"),o=Mo.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 ie,jn,Un,fh,kt,Mo,hh,Do=W(()=>{"use strict";y();Fr();ie=["GET","REST","MCP"],jn=["GET"],Un=["REST","MCP"],fh=["GET","REST"],kt=["admin"];a(_,"e");Mo=[_("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",[...ie,"admin"],"warning"),_("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",kt),_("deploy_outcome_unknown",409,"A past-cap deployment can still change live flows","All approvals require settlement, confirm live checked with the matching attempt, restart, or an audited override; rejection is allowed with a warning",kt),_("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",kt),_("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",[...ie,"admin"],"warning"),_("proposal_conflict",409,"Editor and proposal changes conflict (conflicts contains field names only)","Read the current flow and resolve the proposal before approval",[...ie,"admin"]),_("rebase_ack_required",409,"The merged result needs review (mergedHash included)","Review the diff and approve with its mergedHash",["admin"]),_("rebase_race",409,"Flows kept changing during approval","Recheck the proposal and retry approval",["admin"]),_("conflict_not_found",409,"The requested conflict no longer exists","Reload the proposal and its conflicts",[...Un,"admin"]),_("invalid_choice",400,"Invalid manual conflict choice","Use nodeId plus an available live or proposed choice, without replacement payload or revision",[...Un,"admin"]),_("flow_id_mismatch",400,"Resolution tried to change the proposal flow id","Keep the same flowId or submit a new proposal",Un),_("access_link_missing",401,"No Bearer / path token","Ask the user for a new access link of the right kind"),_("bearer_scheme_required",401,"Auth used Basic (or non-Bearer)","Send `Authorization: Bearer <token>`",Un),_("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`)"),_("access_link_invalid",401,"Unknown, revoked, or cleared after restart","Ask the user for a new access link"),_("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"),_("ai_features_disabled",403,"Plugin AI features off","Owner enables AI Access / `adminAuth`"),_("auth_required",401,"Admin session required","Sign in to the Node-RED editor",kt),_("insufficient_permissions",403,"Admin lacks required scope","Need `*` for link mint / approve",kt),_("invalid_link_kind",400,"Unknown `kind` on create","Use `get_link`, `rest`, or `mcp`",kt),_("invalid_ttl",400,"`ttlSeconds` is zero, negative, not a number, or over the cap for the kind",on,kt),_("access_link_limit",409,"Too many active links","Revoke an existing link first",kt),_("access_link_not_found",404,"Unknown link id on revoke","List links and use an existing id",kt),_("link_create_not_supported",403,"Attempted mint under a GET link","Admin mints via `POST /mcp/api/v1/links` only",jn),_("rate_limited",429,"Per-IP or per-token limit","Wait `retryAfter` / `Retry-After`"),_("payload_too_large",413,"Body / proposal over ~1.5MB","Shrink content"),_("invalid_json",400,"Malformed JSON body","Fix Content-Type / body",Un),_("unsupported_media_type",415,"POST body is not `application/json`","Send `Content-Type: application/json`",Un),_("url_too_long",414,"GET URL over soft cap (~8KB)","Use chunked drafts or REST/MCP",jn),_("not_found",404,"Unknown path or resource","Re-read docs / discovery"),_("missing_query",400,"Required search `q` missing","Pass `?q=`",fh),_("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"),_("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"]),_("invalid_diff_format",400,"Unknown `format` on proposal diff","Use `format=summary` (default) or `format=full`"),_("confirm_required",400,"GET withdraw without `confirm=1`","Repeat the request with `confirm=1`",jn),_("missing_expected_rev",400,"Proposal submitted without `expectedRev` / `rev`","Read the flow and pass its `rev`"),_("version_mismatch",409,"Unknown or unavailable `rev` / `expectedRev` snapshot","Re-read flows and resubmit"),_("proposal_not_found",404,"Unknown proposal id","List proposals and use an existing id"),_("proposal_not_pending",409,"Action needs an open proposal","Only pending or conflict proposals can withdraw / decide"),_("proposal_not_owned",403,"Resolve or withdraw another token\u2019s proposal","Use the creating token; lost-token withdrawal requires a full admin session"),_("validation_failed",400,"Several validation errors at once (message `N validation errors`)","Fix every entry in errors[] and resubmit."),_("duplicate_node_id",400,"The same node id appears more than once in a full tab or batch","Give every node a unique id"),_("missing_node_id",400,"Node / op without an id","Include a non-empty string id"),_("missing_node_type",400,"Node without a `type`","Set `type` to an installed palette type name"),_("invalid_node_type",400,"`type` is not a string","Use the palette type name exactly"),_("invalid_nodes",400,"`flow.nodes` / `flow.configs` not an array","Send arrays (use `[]` to clear)"),_("invalid_node",400,"Node entry is not an object","Send `{ id, type, \u2026 }` objects"),_("invalid_wires",400,"Bad wires shape, missing target, or more ports than outputs","Wire only to existing node ids / ports"),_("z_mismatch",400,"Node `z` differs from the flow id","Set `z` to the tab id or omit it"),_("node_already_exists",400,"`add` op for an id already in the flow","Use `set` or a new id"),_("protected_property",400,"Tried to change id / type / z","Omit id, type, and z from edits"),_("node_not_found",404,"Unknown node (single error only; several errors \u2192 `validation_failed`)","Re-read the flow"),_("field_not_found",404,"Unknown field on a node","Re-read the node"),_("text_not_found",400,"`replace_text` find had no match","Re-read field; find is exact"),_("ambiguous_match",409,"`replace_text` matched more than once","Pass `occurrence` (1-based) or unique find"),_("unknown_node_type",400,"Proposal uses a type that is not installed","List node-types / palette search; ask user to install"),_("node_type_not_found",404,"Unknown type on node-types detail","List types or search palette"),_("chunk_conflict",409,"Same draft seq re-sent with different data","Re-send identical data or start a new draft",jn),_("missing_chunks",400,"Draft submit with gaps in seq","Upload every seq from 0 through max",jn),_("draft_hash_mismatch",400,"Draft sha256 does not match assembled bytes","Send the hex sha256 of the assembled UTF-8",jn),_("missing_config_node",201,"Warning: node has no (or an unresolved) config node yet","Configure before deploy if needed; see `candidates`",ie,"warning"),_("unwired_node",201,"Warning: node has outputs but no wires","Wire it, or ignore while scaffolding",ie,"warning"),_("incomplete_wires",201,"Warning: fewer wire ports than outputs","Add ports if outputs should connect",ie,"warning"),_("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",ie),_("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",ie,"warning"),_("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",ie,"warning"),_("secret_wires_changed",201,"Warning: a node holding a restored secret had its output wires changed","Reviewer must check where its messages now go",ie,"warning"),_("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}`)",ie,"warning"),_("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",ie,"warning"),_("secret_tab_changed",201,"Warning: proposal with restored secrets also changes other nodes in the tab","Review every change before approving",ie,"warning"),_("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",ie,"warning"),_("conflict",200,"Status: open proposal has conflicting editor changes; approval blocked","Read the current flow and resolve the proposal",ie,"status"),_("expired",200,"Status: open proposal expired after the configured TTL (default 7 days)","Re-read the flow and submit a new proposal",ie,"status"),_("superseded",200,"Legacy closed status; new approvals rebase other open proposals","Re-read the flow and submit a new proposal if still needed",ie,"status"),_("upstream_error",502,"Node-RED runtime/flows error","Retry; check Node-RED logs"),_("internal_error",500,"Unexpected server error","Retry; check Node-RED logs")],hh=new Map(Mo.map(e=>[e.code,e]));a(Or,"registryHint");a(Zd,"cell");a(Xd,"renderGetLinkErrorTable")});function Ho(e){let t=ce(e),n=xo(e)||void 0;return{proposalId:e.id,title:t.title,displayName:t.displayName,flowId:e.payload.flowId,...n?{flowLabel:n}:{}}}function Ii(e,t){return e!==void 0&&Number.isFinite(e)&&e>0?e:t}var el,sn,an,$t,dn,At,jo,mh,Qd,Uo,Bo=W(()=>{"use strict";y();el=require("crypto");Ee();Ps();tt();qe();On();lt();Ne();a(Ho,"auditProposalMeta");sn=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}},an=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"}},$t=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"}},dn=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"}},At=class extends Error{static{a(this,"ProposalDeployOutcomeUnknownError")}code="deploy_outcome_unknown";statusCode=409;constructor(){super("A past-cap deployment can still change live flows. Wait for settlement or explicitly override after reviewing the risk.")}},jo=class extends Error{static{a(this,"DeployHardCapError")}},mh=3;a(Ii,"deadlineMs");Qd=Promise.resolve(),Uo=class{constructor(t,n,r,o,s={}){this.proposals=t;this.audit=n;this.adapter=r;this.getDeploymentType=o;this.serviceOptions=s;fa(we(r))}static{a(this,"ProposalService")}unknownDeployBlocks(){return this.proposals.list().filter(t=>t.deployPending?.queueReleased)}confirmLiveChecked(t,n,r){let o=this.proposals.get(t),s=o.deployPending?.attemptId;if(!o.deployPending?.queueReleased||!s||r!==s)throw new At;return this.audit.append("proposal.live_checked",n,{...Ho(o),attemptId:s}),this.proposals.recordDeployPending(t,void 0)}guardUnknown(t=!1){let n=this.unknownDeployBlocks();if(!t&&n.length)throw new At;return n}async approve(t,n,r={}){let o=this.proposals.get(t);this.guardUnknown(r.overrideDeployOutcomeUnknown);let s=o.deployPending!==void 0;if(!U(o.status))throw new Q(t,o.status);if(!s&&this.proposals.isApproving(t))throw new Le(t);let i=Qd,d,l=new Promise(c=>{d=c});return Qd=i.then(()=>l),new Promise((c,u)=>{let p=!1,f,h=new Promise((g,w)=>{f=setTimeout(()=>w(new dn),Ii(this.serviceOptions.approveQueueTimeoutMs,6e4)),f.unref?.()});a(async()=>{try{await Promise.race([i,h]),clearTimeout(f);let g=this.proposals.get(t);if(!U(g.status)&&g.status!=="approved")throw new Q(t,g.status);let w=this.guardUnknown(r.overrideDeployOutcomeUnknown);if(r.overrideDeployOutcomeUnknown&&w.length&&this.audit.append("proposal.deploy_unknown_override",n,{...Ho(g),skippedAttempts:w.map(k=>({proposalId:k.id,attemptId:k.deployPending?.attemptId,flowId:k.payload.flowId}))}),s&&g.status==="approved")return g;let v=this.proposals.beginApprove(t);return p=!0,await this.approveLocked(v,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=Ho(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(Lo(t))throw this.proposals.releaseLock(s),se(this.proposals,{flowId:t.payload.flowId}),new Q(s,this.proposals.get(s).status);let u=we(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<mh;f++){let h=await u.getFlows(),m=this.proposals.get(s),g=st(h.flows,m.payload.flowId,m.payload.flow),w=g&&wt(g);if(w&&(w===m.deployPending?.mergedHash||w===wt(m.payload.flow))){i=p(m,h.rev);break}let{result:v}=We(this.proposals,m,h,{duringApprove:!0});if(v.state==="conflict"||!v.merged||!v.mergedHash)throw this.audit.append("proposal.conflict",n,{...d,liveRev:h.rev,conflictCount:v.conflicts.length}),new et(s,vi(v).conflicts);if(w===v.mergedHash){i=p(m,h.rev);break}if((v.state==="merged"||r.mergedHash!==void 0)&&r.mergedHash!==v.mergedHash)throw new sn(s,v.mergedHash,v.state);let $=v.merged,C=v.mergedHash,O=(0,el.randomUUID)(),q=a(()=>{let P=this.proposals.get(s);return U(P.status)&&P.deployPending?.attemptId===O},"ownsOutcome");m.deployPending&&this.proposals.recordDeployPending(s,{...m.deployPending,attemptId:O});let T=!1,S=new Map,F=a(P=>JSON.stringify([P.status,P.decidedBy,P.reason,P.resultRev]),"decisionKey"),N=a(()=>{this.proposals.get(s).deployPending?.attemptId===O&&this.proposals.recordDeployPending(s,void 0)},"clearUnknown");try{let P=await this.withDeployDeadline(()=>u.updateFlow({flowId:m.payload.flowId,flow:$,expectedRev:h.rev,deploymentType:l}),()=>{T=!0;let A=new $t;this.proposals.recordDeployPending(s,{mergedHash:C,attemptId:O,reason:"Deploy outcome unknown: the runtime has not settled. Approvals are waiting."}),this.audit.append("proposal.deploy_timeout",n,{...d,code:A.code}),o(A)},()=>{S=new Map(this.proposals.list().map(A=>[A.id,F(A)])),this.proposals.recordDeployPending(s,{mergedHash:C,attemptId:O,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 $t)},async A=>{let I=pa(u,A.rev)??h,b=await u.getFlows().catch(()=>{}),M=new Set,H=new Set([...I.flows,...b?.flows??[]].filter(G=>G.type==="tab").map(G=>G.id));for(let G of H){let Ve=st(I.flows,G,{}),qt=b&&st(b.flows,G,{});(!b||wt(Ve??{})!==wt(qt??{}))&&M.add(G)}let L=this.proposals.get(s),ee=L.status==="approved"&&b?.rev===A.rev&&L.resultRev!==void 0&&(L.rebase?.mergedHash===C||wt(L.payload.flow)===C)&&wt(st(b.flows,m.payload.flowId,$)??{})===C,Se=this.proposals.list().filter(G=>M.has(G.payload.flowId)&&!U(G.status)&&S.get(G.id)!==F(G));if(L.status==="withdrawn"&&this.proposals.flagLandedAfterWithdraw(s),Se.length||[...M].some(G=>G!==m.payload.flowId)){let G=`Late deployment of ${s} changed live tabs after the hard cap. Check live flows; intervening edits or decisions may have been overwritten.`;for(let Ve of Se)(Ve.id!==s||Ve.status!=="withdrawn")&&this.proposals.flagDeployment(Ve.id,G);U(L.status)&&this.proposals.flagDeployment(s,G),this.audit.append("proposal.late_deploy_overwrite",n,{...d,attemptId:O,affectedProposalIds:Se.map(Ve=>Ve.id),changedFlowIds:[...M],reason:G}),console.warn(G)}if(ee&&(this.proposals.markApproved(s,L.decidedBy??n,A.rev),this.audit.append("proposal.late_deploy_already_applied",n,{...d,attemptId:O,resultRev:A.rev})),!q()){N();return}this.proposals.markApproved(s,n,A.rev),this.audit.append("proposal.approved",n,{...d,resultRev:A.rev,baseRev:m.expectedRev,liveRev:h.rev,rebaseState:v.state})},()=>{N()});i=this.proposals.markApproved(s,n,P.rev),this.audit.append("proposal.approved",n,{...d,resultRev:P.rev,flowId:m.payload.flowId,baseRev:m.expectedRev,liveRev:h.rev,rebaseState:v.state});break}catch(P){if(i||P instanceof jo)throw P;if(T)throw this.proposals.recordDeployPending(s,void 0),P;if(P instanceof re)continue;let A=P instanceof Error?P.message:String(P),I=this.proposals.markFailed(s,n,A);return this.audit.append("proposal.failed",n,{...d,reason:A}),I}}if(!i)throw new an(s);try{await _r(this.proposals,this.adapter,{flowId:i.payload.flowId})}catch{}return i}async withDeployDeadline(t,n,r,o,s){let i=setTimeout(n,Ii(this.serviceOptions.deployTimeoutMs,6e4));i.unref?.();let d=!1,l,c=new Promise((u,p)=>{l=setTimeout(()=>{try{d=!0,r()}finally{p(new jo)}},Ii(this.serviceOptions.deployHardCapMs,3e5)),l.unref?.()});try{let u=t();return u.then(p=>{if(d)return o(p)},()=>{d&&s()}).catch(()=>{}),await Promise.race([u,c])}finally{clearTimeout(i),clearTimeout(l)}}reject(t,n,r){let o=this.proposals.get(t),s=this.unknownDeployBlocks();if(!U(o.status))throw new Q(t,o.status);let i=Ho(o),d=this.proposals.markRejected(t,n,r.trim()||"Rejected by admin");return s.length&&(d=this.proposals.flagRejectedWhileDeployUnknown(t)),this.audit.append("proposal.rejected",n,{...i,reason:d.reason,rejectedWhileDeployUnknown:d.rejectedWhileDeployUnknown}),d}}});function Ae(e,t,n={}){return Li(e,t,n)}function gh(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 yh(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 Mt(e,t,n,r){return e==="mcp"?t:e==="rest"?n:r}function Li(e,t,n={}){switch(e){case"version_mismatch":return Mt(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 Mt(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 Mt(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 Mt(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 gh(t,n.cause);case"node_type_not_found":case"unknown_node_type":return yh(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 Mt(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 Mt(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 Mt(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 Mt(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 Nr(e,t,n={}){let r=n.canonicalizeMessage!==!1,o=null;if(e instanceof et?o={statusCode:409,body:{error:e.message,code:e.code,conflicts:e.conflicts}}:e instanceof sn?o={statusCode:409,body:{error:e.message,code:e.code,mergedHash:e.mergedHash}}:e instanceof an||e instanceof $t||e instanceof dn||e instanceof Le?o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}:e instanceof x?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,...e.details??{}}}:e instanceof re?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,expectedRev:e.expectedRev,currentRev:e.currentRev}}:e instanceof te?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,flowId:e.flowId}}:e instanceof ye||e instanceof _t||e instanceof Q||e instanceof he?o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}:(e instanceof Ze||e instanceof je)&&(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=ut[s];i&&(o.body.error=i)}if(typeof o.body.hint!="string"||!o.body.hint){let i=Li(s,t,{cause:o.body.cause})??Or(s);i&&(o.body.hint=i)}return o}function tl(e,t,n={}){let o={error:n.error??ut[t]??"Request failed",code:t,...n.details??{}},s=Li(t,e,{cause:o.cause})??Or(t);return s&&(o.hint=s),{statusCode:n.statusCode??400,body:o}}var ut,ct=W(()=>{"use strict";y();Ee();tt();de();le();Do();Bo();ut={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_outcome_unknown:"A past-cap deployment may still change live flows. Wait for settlement or explicitly override after reviewing the risk.",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(Ae,"channelErrorHint");a(gh,"invalidWiresHint");a(yh,"nodeTypeHint");a(Mt,"pickChannel");a(Li,"hintFor");a(Nr,"mapErrorForChannel");a(tl,"buildChannelError")});var nw={};la(nw,{ACCESS_LINK_INVALID_MESSAGE:()=>zt,ACCESS_LINK_MISSING_MESSAGE:()=>He,AccessLinkStore:()=>gn,EDITOR_SIDEBAR_ASSET_VERSION:()=>Wn,MAX_PROPOSAL_JSON_BYTES:()=>Y,SettingsStore:()=>An,acceptedMediaTypes:()=>uu,areAiFeaturesEnabled:()=>De,bindSettingsStore:()=>to,createPluginServices:()=>As,default:()=>tw,getStorageDir:()=>fs,isAdminAuthEnabled:()=>ae,isMcpStreamRequest:()=>pu,packageVersion:()=>qn,registerRoutes:()=>fu,renderEditorSidebarJs:()=>qo,renderEditorSidebarLoaderHtml:()=>sl,requireAccessLink:()=>Pn,resolveAiAccessSettings:()=>Me});module.exports=vu(nw);y();var cu=require("fs");Ps();y();var Gr=require("fs"),sr=require("path");de();function Kr(e){let t=(0,sr.join)(e,".ai-access");return{root:t,proposals:(0,sr.join)(t,"proposals"),logs:(0,sr.join)(t,"logs"),settings:(0,sr.join)(t,"config.json")}}a(Kr,"resolveAiAccessPaths");function Cs(e){(0,Gr.mkdirSync)(e,{recursive:!0,mode:_s});try{(0,Gr.chmodSync)(e,_s)}catch{}}a(Cs,"mkdirSecure");function ya(e){Cs(e.root),Cs(e.proposals),Cs(e.logs)}a(ya,"ensureAiAccessDirs");Gt();y();Ye();function so(e){let t=Pe(e);return`auth-tokens${(t==="/"?"":t).replace(/\//g,"-")}`}a(so,"editorAuthTokenKey");Ye();y();ct();function wh(e){if(!e)return!1;let t=e.split(";")[0]?.trim().toLowerCase()??"";return t==="application/json"||t.endsWith("+json")}a(wh,"isJsonContentType");function bh(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(bh,"hasRequestBody");function Fe(e="rest"){return(t,n,r)=>{let o=t.headers["content-type"];if(wh(o)){r();return}if(!o&&!bh(t)){r();return}let s=tl(e,"unsupported_media_type",{statusCode:415});n.status(s.statusCode).json(s.body)}}a(Fe,"requireJsonContentType");var vh="text/plain; charset=utf-8",kh="text/html; charset=utf-8",Ah="default-src 'none'; base-uri 'self'; form-action 'none'; frame-ancestors 'none'";function xh(e){let t=e.query?.format;return typeof t=="string"?t.toLowerCase():void 0}a(xh,"formatParam");function Sh(e){let t=xh(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(Sh,"chooseGetLinkRepresentation");function nl(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}a(nl,"escapeHtml");var Rh=/(?<![A-Za-z0-9_.~\-/])\.\/[A-Za-z0-9._~\-/]+(?:\?[A-Za-z0-9._~\-=&%]+)?/g;function Th(e){return e.replace(Rh,(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(Th,"linkifyEscaped");function Ph(e,t){return`<!doctype html>
11
+ <html><head><meta charset="utf-8">${t?`<base href="${nl(t)}">`:""}<title>Node-RED AI Access</title></head><body><pre>${Th(nl(e))}</pre></body></html>
12
+ `}a(Ph,"renderGetLinkHtml");function _h(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(_h,"getLinkBaseHref");function Ch(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}a(Ch,"prettyJson");function rl(){return(e,t,n)=>{let r=Sh(e),o=_h(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",vh),s(u)):(t.setHeader("Content-Type",kh),t.setHeader("Content-Security-Policy",Ah),s(Ph(l?Ch(u):u,o)))}),n()}}a(rl,"getLinkContentNegotiation");Jr();y();Ye();function Eh(e){let t=_e(e.settings.httpAdminRoot),n=Rt(e.settings.httpAdminRoot),r="0.2.0-beta.9",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(Eh,"buildDiscoveryIndex");function Ni(e){return(t,n)=>{n.status(200).json(Eh(e))}}a(Ni,"discoveryIndexHandler");function ol(){return(e,t)=>{t.status(404).json({error:"Not found",code:"not_found"})}}a(ol,"mcpApiNotFoundHandler");y();y();var Bn;function qn(){return Bn||(Bn="0.2.0-beta.9",Bn)}a(qn,"packageVersion");var Wn=qn();function Ih(){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(gh,"editorSidebarStyles");function yh(){return`
168
+ }`}a(Ih,"editorSidebarStyles");function Lh(){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(yh,"editorSidebarScriptPartA");function wh(){return`
466
+ `}a(Lh,"editorSidebarScriptPartA");function Nh(){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(wh,"editorSidebarScriptPartB");function bh(){return yh()+wh()}a(bh,"editorSidebarScriptBody");function vh(){return`(function () {
568
+ `}a(Nh,"editorSidebarScriptPartB");function Fh(){return Lh()+Nh()}a(Fh,"editorSidebarScriptBody");function Oh(){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(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>
599
+ })();`}a(Oh,"editorRevealListenerScript");function qo(){let e=JSON.stringify(Ih());return[`/* @com55/node-red-ai-access editor sidebar v=${Wn} */`,"(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);"," }","})();",Oh(),Fh()].join(`
600
+ `)}a(qo,"renderEditorSidebarJs");function sl(e=Wn){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(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)
634
+ `}a(sl,"renderEditorSidebarLoaderHtml");y();le();ct();Ne();function il(e){return ut[e]??"Request failed"}a(il,"safeErrorText");function $h(e,t){let n=J(t,"rest");if(!n)return!1;let r=n.body.code,o={error:il(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($h,"sendMappedFlowError");function al(e){return async(t,n)=>{try{n.json(await vt(rt(t,e)))}catch(r){if($h(n,r))return;n.setHeader("X-Content-Type-Options","nosniff"),n.status(502).json({error:il("upstream_error"),code:"upstream_error"})}}}a(al,"listFlowsHandler");y();Re();le();Ye();ct();y();function zn(e){return e==null||e===""?{ok:!0,format:"summary"}:e==="summary"||e==="full"?{ok:!0,format:e}:{ok:!1}}a(zn,"resolveDiffFormat");function Mh(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(Mh,"compactLineDiff");function Dh(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=Mh(e.lines),d={key:e.key,kind:"text",beforeBytes:o,afterBytes:s,lines:i.lines};return i.truncated&&(d.truncated=!0),d}a(Dh,"formatTextChange");function Hh(e,t){return e.map(n=>n.kind==="text"?Dh(n,t):n)}a(Hh,"formatPropertyChanges");function dl(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(dl,"formatPresenceText");function Fi(e,t){return{summary:e.summary,...e.tabProperties?{tabProperties:e.tabProperties}:{},added:e.added.map(n=>dl(n,t)),removed:e.removed.map(n=>dl(n,t)),modified:e.modified.map(n=>({...n,changes:Hh(n.changes,t)}))}}a(Fi,"formatSections");function Gn(e,t="summary"){let n=Fi(e,t),r={...e,...n,format:t};return e.liveChanges&&(r.liveChanges=Fi(e.liveChanges,t)),e.vsCurrent&&(r.vsCurrent=Fi(e.vsCurrent,t)),r}a(Gn,"applyDiffFormat");y();de();Re();Do();function Kn(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}a(Kn,"escapeForDocs");var ll="https://YOUR-NODE-RED/mcp/rpc";function jh(e,t,n,r=ll){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(Th,"docsAlsoAvailable");function Rh(e,t){return`# Node-RED AI Access \u2014 self-contained GET link (get_link)${e}
704
+ `}a(jh,"docsAlsoAvailable");function Uh(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(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
782
+ `}a(Uh,"docsIntro");function Bh(){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(Bh,"docsWorkflow");function qh(){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(qh,"docsUrlTable");function Wh(){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,11 +857,11 @@ 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(Ch,"docsProposeExamples");function Eh(){return`## Chunked drafts (long HTML / large fields)
860
+ `}a(Wh,"docsProposeExamples");function zh(){return`## Chunked drafts (long HTML / large fields)
861
861
 
862
- When the GET URL would exceed **${wn} bytes**, do not
862
+ When the GET URL would exceed **${Tn} bytes**, do not
863
863
  stuff everything into \`propose\`. Use drafts (expire after **30 minutes**,
864
- max **${K}** decoded bytes):
864
+ max **${Y}** decoded bytes):
865
865
 
866
866
  ### Recommended chunk size
867
867
 
@@ -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(Eh,"docsDrafts");function Ih(){return`## Last resort: content larger than chunked drafts
909
+ `}a(zh,"docsDrafts");function Gh(){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,9 +920,9 @@ 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(Ih,"docsLastResortPaste");function Lh(){return`## Size cap and REST/MCP nodes payload shapes
923
+ `}a(Gh,"docsLastResortPaste");function Kh(){return`## Size cap and REST/MCP nodes payload shapes
924
924
 
925
- The proposal JSON cap is **${K} bytes**
925
+ The proposal JSON cap is **${Y} bytes**
926
926
  (\`MAX_PROPOSAL_JSON_BYTES\` / ~1.5MB) on **every** channel: GET propose/drafts,
927
927
  REST \`POST /proposals\`, and MCP \`submit_proposal\`. Oversized bodies return
928
928
  \`413 payload_too_large\` (never truncated).
@@ -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(Lh,"docsRestNodesPayload");function Nh(){return`## Known issues
969
+ `}a(Kh,"docsRestNodesPayload");function Jh(){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(Nh,"docsKnownIssues");function Fh(){return`## Error codes (JSON unless docs)
977
+ `}a(Jh,"docsKnownIssues");function Vh(){return`## Error codes (JSON unless docs)
978
978
 
979
- ${Hd()}
979
+ ${Xd()}
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(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(`
983
+ `}a(Vh,"docsErrorsAndAlso");function cl(e){let{link:t,openapiPath:n,restApiBase:r,mcpRpcPath:o,publicOrigin:s}=e,i=Kn(t.expiresAt),d=t.label?` (${Kn(t.label)})`:"",l=Kn(n),c=Kn(r),u=Kn(o);return[Uh(d,i),jh(c,l,u,s?Kn(`${s}${o}`):ll),Bh(),qh(),Wh(),Kh(),zh(),Gh(),Jh(),Vh()].join(`
984
+ `)}a(cl,"renderGetLinkDocs");y();var kl=require("crypto");de();le();ct();en();qe();lt();y();le();function Wo(e){return{error:e instanceof Error&&e.message.trim()?e.message:"Internal server error",code:"internal_error"}}a(Wo,"internalErrorBody");function Yh(e,t){e.headersSent||e.status(500).json(Wo(t))}a(Yh,"sendInternalError");function z(e,t){if(e.headersSent)return;let n=J(t);if(n){e.status(n.statusCode).json(n.body);return}Yh(e,t)}a(z,"sendCaughtRouteError");function D(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}z(n,o)})}}a(D,"asyncRoute");y();var Go=require("crypto");de();Re();var V=class extends Error{static{a(this,"DraftValidationError")}statusCode=400;code;constructor(t,n="draft_invalid"){super(t),this.name="DraftValidationError",this.code=n}},ln=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"}},cn=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}},Zh=/^[A-Za-z0-9_-]+={0,2}$/;function ul(e){if(!Zh.test(e)||e.replace(/=+$/,"").length===0)throw new V("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 V("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data")}if(t.byteLength===0&&e.length>0)throw new V("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data");return t}a(ul,"assertValidBase64UrlChunk");var zo=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,Go.randomBytes)(12).toString("hex")),this.maxBytes=t.maxBytes??Y,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 cn(t,o.proposalId);let s=this.drafts.get(t);if(s?.linkId!==n)throw new ln(t);if(s.expiresAtMs<=r)throw new ln(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 V("seq must be a non-negative integer","invalid_seq");if(typeof o!="string")throw new V("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 V(`Chunk seq ${r} already stored with different data`,"chunk_conflict")}else{let u=ul(o),p=s.totalBytes+u.byteLength;if(p>this.maxBytes)throw new V(`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 V("Draft has no chunks","draft_empty");let i=[...o.chunks.keys()].sort((h,m)=>h-m).at(-1);if(i===void 0)throw new V("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 V(`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 V(`Missing chunk seqs: ${h}`,"missing_chunks");l.push(m)}let c=l.map(h=>ul(h)),u=Buffer.concat(c);if(u.byteLength>this.maxBytes)throw new V(`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 V("invalid_sha256: expected 64 hex chars","invalid_sha256");if((0,Go.createHash)("sha256").update(u).digest("hex")!==p)throw new V("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()}};No();y();Gt();Re();y();var Dt=rr(require("express-rate-limit"),1);var $i=6e4,pl=600,fl=300;function Oi(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?Math.max(1,Math.floor(e)):t}a(Oi,"positiveInt");function xt(e){if(!e)return{windowMs:$i,maxPerIp:pl,maxPerToken:fl};let t=e;return{windowMs:Oi(t.windowMs??t.rateLimitWindowMs,$i),maxPerIp:Oi(t.maxPerIp??t.rateLimitMaxPerIp,pl),maxPerToken:Oi(t.maxPerToken??t.rateLimitMaxPerToken,fl)}}a(xt,"resolveRateLimitConfig");function Xh(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(Xh,"retryAfterSeconds");function hl(e,t,n=$i){let r=Xh(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(hl,"sendRateLimited");function Qh(e){return(t,n,r,o)=>{hl(t,n,e)}}a(Qh,"rateLimitedHandler");function Ko(e){return`ip:${(0,Dt.ipKeyGenerator)(e.ip??"unknown")}`}a(Ko,"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 ml(e,t,n,r){let o={windowMs:t,max:e,standardHeaders:!0,legacyHeaders:!1,keyGenerator:n,handler:Qh(t)};return r&&(o.skip=r),(0,Dt.default)(o)}a(ml,"createLimiter");function Mi(e){let t=xt(e);return ml(t.maxPerIp,t.windowMs,Ko)}a(Mi,"createAiAccessRateLimiter");function em(e){let t=xt(e);return ml(t.maxPerToken,t.windowMs,n=>Jo(n)??Ko(n),n=>Jo(n)==null)}a(em,"createAiAccessTokenRateLimiter");function gl(e){let t=xt(e),n=Mi(t),r=em(t);return{ip:n,token:r,stack:[n,r],config:t}}a(gl,"createAiAccessDualRateLimiters");function yl(e){let t=xt(e()).windowMs,n=xt(e()).maxPerIp,r=xt(e()).maxPerToken,o=new Dt.MemoryStore,s=new Dt.MemoryStore,i=a(()=>{let m=xt(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=((w,v)=>(w.toLowerCase()==="ratelimit-policy"&&typeof v=="string"&&(v=v.replace(/;w=\d+/,`;w=${Math.ceil(t/1e3)}`)),g(w,v)))},"useLiveWindowPolicy"),c=a((m,g)=>(0,Dt.default)({store:g,windowMs:t,limit:a(()=>{let w=xt(e());return m?w.maxPerToken:w.maxPerIp},"limit"),standardHeaders:!0,legacyHeaders:!1,keyGenerator:m?w=>Jo(w)??Ko(w):Ko,skip:a(w=>m&&Jo(w)==null,"skip"),handler:a((w,v,k)=>{hl(w,v,xt(e()).windowMs)},"handler")}),"make"),u=c(!1,o),p=c(!0,s);return{ip:a((m,g,w)=>(i(),l(g),u(m,g,w)),"ip"),token:a((m,g,w)=>(i(),l(g),p(m,g,w)),"token")}}a(yl,"createLiveAiAccessDualRateLimiters");Te();function K(e){e.setHeader("Cache-Control","no-store"),e.setHeader("Pragma","no-cache")}a(K,"setNoStore");function Di(e){let t=e.originalUrl||e.url||"",n=Os(t);n!==t&&(Object.defineProperty(e,"originalUrl",{value:n,writable:!0,configurable:!0}),typeof e.url=="string"&&Object.defineProperty(e,"url",{value:Os(e.url),writable:!0,configurable:!0}))}a(Di,"redactTokenFromRequestUrl");function wl(e,t){return(n,r,o)=>{K(r);let s=n.originalUrl||n.url||"";if(n.getLinkUrlBytes=Buffer.byteLength(s,"utf8"),Di(n),!De(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",ge),r.status(401).json({error:He,code:ht,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"){fr(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",ge),r.status(401).json({error:zt,code:pr,hint:c});return}if(l.link.kind!=="get_link"){hr(r,"get_link",l.link.kind);return}n.accessLink=l.link,n.getLinkToken=d,o()}}a(wl,"requireGetLinkToken");function Jn(e,t){let n=e.getLinkUrlBytes;return(typeof n=="number"?n:Buffer.byteLength(e.originalUrl||e.url||"","utf8"))>Tn?(K(t),t.status(414).json({error:`Request URL exceeds ${Tn} 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:Tn}),!0):!1}a(Jn,"rejectOversizedGetUrl");function B(e,t,n,r,o,s){K(e),e.status(t).json({error:n,code:r,...o?{hint:o}:{},...s})}a(B,"jsonError");function bl(){return(e,t)=>{Di(e),K(t),t.status(404).json({error:"Not found",code:"not_found"})}}a(bl,"getLinkNotFoundHandler");var tm="link_create_not_supported";function vl(){return(e,t)=>{Di(e),K(t),t.status(403).json({error:"GET access links cannot create tokens",code:tm,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(vl,"getLinkMintNotSupportedHandler");y();function j(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(j,"queryString");function Ke(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(Ke,"draftErrorHint");async function nm(e,t,n){let r;try{r=await e.getFlow(t)}catch(s){let i=J(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(nm,"validateFieldDraftTargets");function rm(e,t,n,r){if(r)try{let o=Er(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=Ke("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(rm,"planDraftMode");async function om(e,t,n,r){try{let o=await nm(t,n,r);if(o){let s=o.code==="node_not_found"||o.code==="field_not_found"?404:400;return B(e,s,o.message,o.code,Ke(o.code)),!0}}catch(o){let s=J(o);return s?(B(e,s.statusCode,s.body.error,s.body.code,Ke(s.body.code)),!0):(z(e,o),!0)}return!1}a(om,"rejectInvalidFieldTargets");function Al(e,t){return async(n,r)=>{if(Jn(n,r))return;K(r);let o=j(n.query,"flow"),s=j(n.query,"rev"),i=j(n.query,"node"),d=j(n.query,"field"),l=j(n.query,"summary"),c=j(n.query,"title")??l,u=j(n.query,"mode"),p=j(n.query,"targets");if(!o||!s){B(r,400,"flow and rev query parameters are required","missing_draft_params",Ke("missing_draft_params"));return}if(!n.accessLink){B(r,401,"Access link required","access_link_missing");return}try{let m=await(await Lr(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:Ke("version_mismatch")});return}}catch(m){let g=J(m);if(g){B(r,g.statusCode,g.body.error,g.body.code,g.body.code==="flow_not_found"?Ae("flow_not_found","get")??"Use an id from GET ./flows":Ke(g.body.code));return}z(r,m);return}let f=rm(i,d,u,p);if(!f.ok){B(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 om(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:Y,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(Al,"getLinkDraftNewHandler");function xl(e){return(t,n)=>{if(Jn(t,n))return;K(n);let r=t.params.draftId,o=j(t.query,"seq"),s=j(t.query,"data");if(typeof r!="string"||!r){B(n,400,"Missing draft id","missing_draft_id");return}if(!t.accessLink){B(n,401,"Access link required","access_link_missing");return}if(o===void 0||s===void 0){B(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 ln||d instanceof cn||d instanceof V){B(n,d.statusCode,d.message,d.code,Ke(d.code),d instanceof cn?{proposalId:d.proposalId}:void 0);return}z(n,d);return}}}a(xl,"getLinkDraftAppendHandler");function sm(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 V("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 V("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 V(`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 V("Invalid multi-target draft JSON","invalid_draft_body")}if(e.nodeId&&e.field)return[{nodeId:e.nodeId,set:{[e.field]:t}}];throw new V("Field draft missing node/field","invalid_draft")}a(sm,"editsFromFieldDraft");function Sl(e,t,n,r,o,s){return async(i,d)=>{if(Jn(i,d))return;K(d);let l=i.params.draftId,c=j(i.query,"sha256"),u=j(i.query,"title");if(typeof l!="string"||!l){B(d,400,"Missing draft id","missing_draft_id");return}if(!i.accessLink){B(d,401,"Access link required","access_link_missing");return}if(!c){B(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,w=await Lr(r,p.expectedRev);if(p.mode==="field"){let S=sm(p,f),F=await Cr(w,p.flowId,S);if(F.expectedRev!==p.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:p.expectedRev,currentRev:F.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=F.flow,g=F.expectedRev}else{let S=Ir(JSON.parse(f),p.flowId);if(h=S.flowId,S.kind==="replace_flow")m={...S.flow,id:S.flow.id||h,type:S.flow.type??"tab",nodes:S.flow.nodes??[]};else{let F=await tn(w,h,S.ops,{expectedRev:p.expectedRev,...s?{nodeTypes:s}:{}});if(F.expectedRev!==p.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:p.expectedRev,currentRev:F.expectedRev,draftDeleted:!1,hint:"Draft was kept. Re-read the flow and start a new draft with the current rev."});return}m=F.flow,g=F.expectedRev}}let v=u??p.title??p.summary,k=Z(i,o),$=ve(i.query),C=await Ot(t,n,i.accessLink,{type:"replace_flow",expectedRev:g,payload:{flowId:h,flow:m},...v?{title:v}:{}},r,k,s);e.markSubmitted(l,i.accessLink.id,C.id);let O={...ue(C,{...k,include:$}),mine:!0},q=se(t),T=it(t,t.get(C.id),i.accessLink.id);d.status(201).json({ok:!0,proposal:O,sha256:(0,kl.createHash)("sha256").update(Buffer.from(f,"utf8")).digest("hex"),...at(T),...me(q),hint:`Waiting for human approval. Tell the user about "${O.displayName}" (${O.reviewUrl}). Poll GET \u2026/proposals/<id> for status.`})}catch(p){if(p instanceof cn){B(d,p.statusCode,p.message,p.code,Ke(p.code),{proposalId:p.proposalId});return}if(p instanceof ln||p instanceof V){B(d,p.statusCode,p.message,p.code,Ke(p.code));return}if(p instanceof SyntaxError){B(d,400,"Assembled draft is not valid JSON","invalid_draft_body");return}let f=J(p);if(f){d.status(f.statusCode).json({...f.body,...Ke(f.body.code)?{hint:Ke(f.body.code)}:{}});return}z(d,p);return}}}a(Sl,"getLinkDraftSubmitHandler");y();le();ct();Do();en();qe();lt();No();var im={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 Hi(e,t){return Ae(e,"get",{cause:t})??im[e]??Or(e)}a(Hi,"proposeErrorHint");function am(e,t,n){e.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:t,currentRev:n,hint:Hi("version_mismatch")})}a(am,"sendVersionMismatch");async function dm(e,t,n,r,o){let s=Ir(Er(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 tn(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(dm,"buildFromPatch");async function lm(e,t,n,r,o){let s=await Cr(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(lm,"buildFromSetProps");async function cm(e,t,n,r,o,s,i,d){let l=await tn(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(cm,"buildFromReplaceText");function um(e){let t=Object.prototype.hasOwnProperty.call(e.query,"replace");return{flowId:j(e.query,"flow"),rev:j(e.query,"rev"),nodeId:j(e.query,"node"),patchEncoded:j(e.query,"patch"),setProps:ki(e.query),findText:j(e.query,"find"),replaceText:t?j(e.query,"replace")??"":void 0,fieldName:j(e.query,"field"),occurrenceRaw:j(e.query,"occurrence")}}a(um,"readProposeQuery");function Je(e,t,n){B(e,400,n,t,Hi(t))}a(Je,"jsonProposeError");function pm(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(pm,"parseOccurrenceQuery");async function fm(e,t,n,r,o){if(n.patchEncoded)return dm(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=pm(e,n.occurrenceRaw);return i.ok?cm(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):lm(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(fm,"buildProposeFromQuery");function Rl(e,t,n,r,o){return async(s,i)=>{if(Jn(s,i))return;K(i);let d=um(s),l=j(s.query,"summary"),c=j(s.query,"title")??l;if(!d.rev){Je(i,"missing_expected_rev","rev query parameter is required");return}if(!s.accessLink){B(i,401,"Access link required","access_link_missing");return}try{let u=await Lr(n,d.rev),p=await fm(i,u,d,d.rev,o);if(!p)return;if(!p.ok){am(i,p.expectedRev,p.currentRev);return}let f=Z(s,r),h=ve(s.query),m=await Ot(e,t,s.accessLink,{type:"replace_flow",expectedRev:p.expectedRev,payload:{flowId:p.builtFlowId,flow:p.builtFlow},...c?{title:c}:{}},n,f,o),g={...ue(m,{...f,include:h}),mine:!0},w=se(e),v=it(e,e.get(m.id),s.accessLink.id),k=m.warnings;i.status(201).json({ok:!0,proposal:g,...l?{summary:l}:{},...at(v),...me(w),...k&&k.length>0?{warnings:k}:{},hint:`Waiting for human approval. Tell the user about "${g.displayName}" (${g.reviewUrl}). Poll GET \u2026/proposals/<id> for status.${k&&k.length>0?` Soft warnings (${k.length}) are non-blocking \u2014 configure later if needed.`:""}`})}catch(u){let p=J(u,"get");if(p){let f=Hi(p.body.code,p.body.cause);i.status(p.statusCode).json({...p.body,...f?{hint:f}:{}});return}z(i,u)}}}a(Rl,"getLinkProposeHandler");y();le();ct();y();le();var hm=50,Tl=40,mm=20;function gm(e){return Buffer.byteLength(JSON.stringify(e),"utf8")}a(gm,"nodeBytes");function ym(e){let t={id:e.id,type:e.type,bytes:gm(e)};return typeof e.name=="string"&&(t.name=e.name),"wires"in e&&(t.wires=e.wires),t}a(ym,"summarizeNode");async function Vo(e,t){let{rev:n,flow:r}=await Ge(e,t),o=r,s=Array.isArray(o.nodes)?o.nodes.map(ym):[],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(Vo,"getFlowSummary");function Pl(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(Pl,"collectTabs");async function _l(e,t,n){if(!t)throw new x("Missing node id","missing_node_id");if(n){let{rev:d,flow:l}=await Ge(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 x("Node was not found","node_not_found",{statusCode:404});return{rev:d,flowId:n,node:f}}let{rev:r,flows:o}=await vt(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 Pl(o)){let l=d.id;if(l)try{let u=(await Ge(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 x("Node was not found","node_not_found",{statusCode:404});return{rev:r,flowId:i,node:s}}a(_l,"findNodeInFlows");async function Yo(e,t,n){let r=await _l(e,t,n);return{rev:r.rev,flowId:r.flowId,node:r.node}}a(Yo,"getNodeById");function wm(e){if(e===void 0||e==="")return;let t=/^(\d+)(?:-(\d+))?$/.exec(e.trim());if(!t)throw new x("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 x("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");if(r<n)throw new x("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");return{start:n,end:r}}a(wm,"parseLineRange");async function Zo(e,t,n,r={}){if(!n)throw new x("Missing field name","missing_field");let o=await _l(e,t,r.flowId);if(!(n in o.node))throw new x("Field was not found on the node","field_not_found");let s=o.node[n];if(typeof s!="string")throw new x("Field is not a string","field_not_string");let i=s.split(`
985
+ `),d=i.length,l=Buffer.byteLength(s,"utf8"),c=wm(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 x(`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(Zo,"getNodeField");function bm(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(bm,"stringFields");function vm(e,t,n){let r=Math.max(0,t-Tl),o=Math.min(e.length,t+n+Tl),s=e.slice(r,o).replace(/\s+/g," ");return r>0&&(s=`\u2026${s}`),o<e.length&&(s=`${s}\u2026`),s}a(vm,"snippetAround");function km(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(km,"lineNumberAtIndex");function ji(e,t,n,r,o){for(let{field:s,value:i}of bm(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(`
987
987
  `)){let f=i.split(`
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=`
988
+ `);for(let h=0;h<f.length;h+=1){let g=(f[h]??"").indexOf(n);if(g>=0){let w=0;for(let v=0;v<h;v+=1)w+=(f[v]?.length??0)+1;u(h+1,w+g)}}}if(n.includes(`
989
+ `)){let f=0;for(;f<=i.length;){let h=i.indexOf(n,f);if(h<0)break;u(km(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,mm),lineCount:p,snippet:vm(i,Math.max(0,c),n.length)})}return r.length>=o}a(ji,"searchInNode");function Cl(e){return[...e.nodes??[],...e.configs??[]]}a(Cl,"nodePool");async function Am(e,t,n,r,o){let s=await Ge(e,t);for(let i of Cl(s.flow))if(ji(i,t,n,r,o))return{rev:s.rev,truncated:!0};return{rev:s.rev,truncated:!1}}a(Am,"searchOneFlow");async function xm(e,t,n,r){let o=await vt(e),s=new Set;for(let i of Pl(o.flows))if(i.id)try{let d=await Ge(e,i.id);for(let l of Cl(d.flow))if(s.add(l.id),ji(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(ji(d,l,t,n,r))return{rev:o.rev,truncated:!0}}return{rev:o.rev,truncated:!1}}a(xm,"searchAllFlows");async function Xo(e,t,n={}){if(typeof t!="string"||!t.trim())throw new x("q query parameter is required","missing_query");let r=typeof n.cap=="number"&&n.cap>0?Math.floor(n.cap):hm,o=[],s=n.flowId?await Am(e,n.flowId,t,o,r):await xm(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(Xo,"searchFlows");function El(e){if(e===void 0)return!1;let t=e.toLowerCase();return t==="1"||t==="true"||t==="yes"}a(El,"isTruthyQueryFlag");Ne();function Sm(e,t,n){switch(e){case"node_not_found":case"flow_not_found":case"field_not_found":return n??Ae(e,t);default:return Ae(e,t)}}a(Sm,"layeredReadErrorHint");function un(e){return ut[e]??"Request failed"}a(un,"safeErrorText");function Oe(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(Oe,"sendJson");function Qo(e,t,n,r){let o=J(t,n);if(!o)return!1;let s=o.body.code,i={error:un(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=Sm(s,n,r);return d&&(i.hint=d),Oe(e,o.statusCode,i),!0}a(Qo,"sendMappedError");function es(e){Oe(e,502,{error:un("upstream_error"),code:"upstream_error"})}a(es,"sendUpstreamError");function ts(e,t="rest"){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){Oe(r,400,{error:un("missing_flow_id"),code:"missing_flow_id"});return}let s=El(j(n.query,"summary"));try{if(s){Oe(r,200,await Vo(rt(n,e),o));return}Oe(r,200,await Ge(rt(n,e),o))}catch(i){if(Qo(r,i,t))return;es(r)}}}a(ts,"getFlowOrSummaryHandler");function ns(e,t="rest"){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||!o){Oe(r,400,{error:un("missing_node_id"),code:"missing_node_id"});return}let s=j(n.query,"flow");try{Oe(r,200,await Yo(rt(n,e),o,s))}catch(i){if(Qo(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;es(r)}}}a(ns,"getNodeHandler");function rs(e,t="rest"){return async(n,r)=>{let o=n.params.id,s=n.params.name;if(typeof o!="string"||!o){Oe(r,400,{error:un("missing_node_id"),code:"missing_node_id"});return}if(typeof s!="string"||!s){Oe(r,400,{error:un("missing_field"),code:"missing_field"});return}let i=j(n.query,"flow"),d=j(n.query,"lines");try{Oe(r,200,await Zo(rt(n,e),o,s,{...i?{flowId:i}:{},...d?{lines:d}:{}}))}catch(l){if(Qo(r,l,t,"Use summary/search to discover node ids and fields."))return;es(r)}}}a(rs,"getNodeFieldHandler");function os(e,t="rest"){return async(n,r)=>{let o=j(n.query,"q");if(!o){Oe(r,400,{error:un("missing_query"),code:"missing_query",hint:t==="get"?"Example: GET \u2026/search?q=TODO&flow=tab1":Ae("missing_query",t)});return}let s=j(n.query,"flow");try{Oe(r,200,await Xo(rt(n,e),o,s?{flowId:s}:{}))}catch(i){if(Qo(r,i,t))return;es(r)}}}a(os,"searchFlowsHandler");y();le();Zt();var Rm={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 Rm[e]??"Request failed"}a($r,"safeErrorText");function Ht(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(Ht,"sendJson");function Ui(e,t){let n=J(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."),Ht(e,n.statusCode,o),!0}a(Ui,"sendMappedError");function ss(e){return async(t,n)=>{try{let r=j(t.query,"q"),o=await bo(e.nodeTypes,r?{q:r}:{});Ht(n,200,o)}catch(r){if(Ui(n,r))return;Ht(n,502,{error:$r("upstream_error"),code:"upstream_error"})}}}a(ss,"listNodeTypesHandler");function is(e){return async(t,n)=>{try{let r=t.params.type,o=typeof r=="string"?decodeURIComponent(r):"",s=j(t.query,"help"),i=!(s==="0"||s==="false"||s==="no"||s==="omit"),d=await Yt(e.nodeTypes,o,{includeHelp:i});Ht(n,200,d)}catch(r){if(Ui(n,r))return;Ht(n,502,{error:$r("upstream_error"),code:"upstream_error"})}}}a(is,"getNodeTypeHandler");function as(e){return async(t,n)=>{try{let r=j(t.query,"q");if(!r){Ht(n,400,{error:$r("missing_query"),code:"missing_query",hint:"Pass ?q= to search the Node-RED catalogue (recommend only)."});return}let o=await vo(e.nodeTypes,r,{...e.catalogueFetcher?{catalogueFetcher:e.catalogueFetcher}:{},...e.catalogueUrl?{catalogueUrl:e.catalogueUrl}:{}});Ht(n,200,o)}catch(r){if(Ui(n,r))return;Ht(n,502,{error:$r("upstream_error"),code:"upstream_error"})}}}a(as,"searchPaletteHandler");en();y();Ee();ao();tt();y();function Vn(e,t){return e[t]??""}a(Vn,"lineAt");function Mr(e,t,n){let r=e[t];return r?r[n]??0:0}a(Mr,"cell");function Il(e,t,n,r){let o=e[t];o&&(o[n]=r)}a(Il,"setCell");function Nl(e,t){let n=Ll(e),r=Ll(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)Vn(n,u)===Vn(r,p)?Il(i,u,p,Mr(i,u+1,p+1)+1):Il(i,u,p,Math.max(Mr(i,u+1,p),Mr(i,u,p+1)));let d=[],l=0,c=0;for(;l<o&&c<s;){let u=Vn(n,l),p=Vn(r,c);u===p?(d.push({op:"equal",text:u}),l+=1,c+=1):Mr(i,l+1,c)>=Mr(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:Vn(n,l)}),l+=1;for(;c<s;)d.push({op:"add",text:Vn(r,c)}),c+=1;return d}a(Nl,"diffLines");function Ll(e){return e.length===0?[]:e.split(`
990
+ `)}a(Ll,"splitLines");qe();On();lt();Ne();ao();var $l=new Set(["func","template","info","payload","code","text","format","syntax"]),Tm=new Set(["x","y","w","h","wires","g"]),Pm=new Set(["w","h","g"]);function pn(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(pn,"nodeName");function Fl(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(Fl,"collectNodes");function us(e){if(e===void 0)return"undefined";if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(us).join(",")}]`;let t=e;return`{${Object.keys(t).sort().map(r=>`${JSON.stringify(r)}:${us(t[r])}`).join(",")}}`}a(us,"stableStringify");function Ml(e,t){return us(e)===us(t)}a(Ml,"valuesEqual");function ps(e){return typeof e=="string"?e:null}a(ps,"asText");function _m(e){return/<\/?[a-z][\s\S]*>/i.test(e.slice(0,800))}a(_m,"looksLikeHtml");function Cm(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:_m(n)}a(Cm,"isHtmlTemplateField");function Dl(e,t,n){let r=ps(n);if($l.has(t)&&r!==null){let o={key:t,kind:"text",value:r};return Cm(e,t,r)&&(o.preview="html"),o}return{key:t,kind:"scalar",value:n===void 0?null:n}}a(Dl,"toPresenceProperty");function Em(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=Hl(e,t);n.push({key:"wires",kind:"scalar",value:o.length>0?o:e.wires??null});continue}n.push(Dl(e,r,e[r]))}return n}a(Em,"nodeProperties");function Hl(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?pn(l)??l.type:d})}r.push({port:s,to:i})}),r}a(Hl,"describeWiresStructured");var Im=new Set(["func","template","code","text"]);function Lm(e,t){let n=[],r=pn(e);r&&n.push({key:"name",kind:"scalar",value:r});let o=Hl(e,t);o.length>0&&n.push({key:"wires",kind:"scalar",value:o});for(let s of Im){if(!(s in e))continue;let i=ps(e[s]);i===null||i.length===0||n.push(Dl(e,s,i))}return n}a(Lm,"buildKeyFields");function Nm(e,t,n){let r=Et(e.type),o=pn(e),s=o?` "${o}"`:"",i=t?` in "${t}"`:"",d=n===1?"1 field":`${n} fields`;return`Changes ${r} node${s}${i} (${d})`}a(Nm,"buildModifiedSummary");function Fm(e,t,n){let r=Et(e.type),o=pn(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(Fm,"buildPresenceSummary");function ds(e,t,n,r){let o={id:e.id,type:e.type,summary:Fm(e,t,n),keyFields:Lm(e,r),properties:Em(e,r)},s=pn(e);return s!==void 0&&(o.name=s),o}a(ds,"toPresenceNode");function Om(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let o of[...n].sort()){if(Tm.has(o)||o==="id"||o==="z")continue;let s=e[o],i=t[o];if(Ml(s,i))continue;let d=ps(s),l=ps(i);if($l.has(o)&&(d!==null||l!==null)){let c=d??"",u=l??"";r.push({key:o,kind:"text",before:c,after:u,lines:Nl(c,u)});continue}r.push({key:o,kind:"scalar",before:s===void 0?null:s,after:i===void 0?null:i})}return r}a(Om,"compareNodes");function Ol(e,t){let n=["label","disabled","info","env"],r=[];for(let o of n){let s=o==="disabled"?!1:o==="env"?[]:"";Ml(e[o]??s,t[o]??s)||r.push(o)}return r}a(Ol,"compareTabMeta");function ls(e){return typeof e.label=="string"&&e.label.trim()?e.label.trim():""}a(ls,"flowLabelOf");function cs(e,t){let n=Fl(t),r=Fl(e),o=ls(e)||ls(t),s=ls(t)||ls(e),i=[],d=[],l=[];for(let c of e.nodes??[])n.has(c.id)||i.push(ds(c,"added",o,r));for(let c of e.configs??[])n.has(c.id)||i.push(ds(c,"added",o,r));for(let c of t.nodes??[])r.has(c.id)||d.push(ds(c,"removed",s,n));for(let c of t.configs??[])r.has(c.id)||d.push(ds(c,"removed",s,n));for(let[c,u]of r){let p=n.get(c);if(!p)continue;let f=Om(p,u);if(f.length>0){let h={id:c,type:u.type||p.type,summary:Nm(u,o||s,f.length),changes:f},m=pn(u)??pn(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:Ol(t,e),beforeNodeCount:n.size,afterNodeCount:r.size},tabProperties:Ol(t,e).map(c=>({key:c,kind:"scalar",before:t[c],after:e[c]})),added:i,removed:d,modified:l}}a(cs,"buildDiffSections");function Bi(e,t,n={}){let r=cs(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(Bi,"buildProposalDiff");async function Yn(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 te))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=we(t),p=await u.getFlows(),f=bi(n,p),h=u!==t,m=h?nt(n):n,g=st(p.flows,r,n.baseFlow)??o,w=h?yt(g).value:g,v=Bi(m,{rev:e.expectedRev,flow:m.baseFlow??w},{currentRev:p.rev,compareAgainst:m.baseFlow?"base":"current",baseSnapshotMissing:m.baseFlow===void 0,baseRevChanged:e.expectedRev!==p.rev});v.rebase={rev:f.rev,state:f.state,conflicts:h?pi(f.conflicts):f.conflicts,...f.mergedHash?{mergedHash:f.mergedHash}:{}};let k=a((C,O)=>O===r?C:[...C?.nodes??[],...C?.configs??[]].find(q=>q.id===O),"target");v.conflictValues=f.conflicts.map(C=>({nodeId:C.nodeId??"",fields:(C.fields?.length?C.fields:[""]).map(O=>({field:O,base:O?Xe(k(m.baseFlow,C.nodeId),O):k(m.baseFlow,C.nodeId),live:O?Xe(k(w,C.nodeId),O):k(w,C.nodeId),proposed:O?Xe(k(m.payload.flow,C.nodeId),O):k(m.payload.flow,C.nodeId)}))}));let $=f.merged?h?yt(f.merged).value:f.merged:m.payload.flow;return v.aiOverrideValues=(m.aiConflictOverrides??[]).map(C=>({nodeId:C.nodeId,fields:C.fields.map(O=>({field:O,live:O?Xe(k(w,C.nodeId),O):k(w,C.nodeId),proposed:O?Xe(k($,C.nodeId),O):k($,C.nodeId)}))})),v.liveChanges=cs(w,m.baseFlow??w),f.state!=="conflict"&&f.merged&&(v.vsCurrent=cs(h?yt(f.merged).value:f.merged,w)),v}if(c!==void 0){let u=l?cs(e.payload.flow,i):void 0;return Bi(e,{rev:e.expectedRev,flow:c},{currentRev:s,compareAgainst:"base",baseRevChanged:l,...u?{vsCurrent:u}:{}})}return Bi(e,{rev:s,flow:i},{compareAgainst:"current",baseSnapshotMissing:!0,baseRevChanged:l})}a(Yn,"computeProposalDiff");qe();lt();y();Ye();Te();y();var jl=require("path");function fs(e){return(0,jl.join)(e.userDir,".ai-access")}a(fs,"getStorageDir");var $m=["status:read","flows:read","proposals:create","proposals:read-own"];function qi(){return qn()}a(qi,"readPackageVersion");function Mm(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/1e3)):0}a(Mm,"secondsRemainingUntil");function Dm(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/6e4)):0}a(Dm,"minutesRemainingUntil");function Hm(e,t){let n=_e(t),r=Rt(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(Hm,"baseUrlsForTokenKind");function Zn(e,t){let n=no(e,t),r=Me(e.settings);if(t.auth){let o=t.auth.extra?.user;return{ok:!0,version:qi(),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:qi(),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:Dm(t.accessLink.expiresAt,o),secondsRemaining:Mm(t.accessLink.expiresAt,o),permissions:[...$m],baseUrls:Hm(t.accessLink.kind,e.settings.httpAdminRoot)}}}return{ok:!0,version:qi(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:n.permission,capabilities:n.capabilities,storageDir:fs(e.settings),defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,maxActiveLinks:r.maxActiveLinks,defaultDeploymentType:r.defaultDeploymentType,caller:{kind:"admin",...n.permission?{permission:n.permission}:{}}}}a(Zn,"buildStatus");function Ul(e,t){let n=t.draftStore??new zo,r=t.rateLimiters??gl(t.rateLimit??e.settings.aiAccess),o=e.httpAdmin,s="/mcp/a/:token",i=[r.ip,wl(e,t.linkStore),r.token];o.use("/mcp/a",rl()),o.get(s,...i,D((u,p)=>{K(p),p.setHeader("X-Content-Type-Options","nosniff");let f=_e(e.settings.httpAdminRoot),h=u.accessLink;if(!h){B(p,401,"Access link required","access_link_missing");return}let m=cl({link:h,openapiPath:`${f}/openapi.yaml`,restApiBase:f,mcpRpcPath:Rt(e.settings.httpAdminRoot),publicOrigin:Fs(u)});p.status(200).type("text/markdown; charset=utf-8").send(m)})),o.get(`${s}/status`,...i,D((u,p)=>{K(p),p.json(Zn(e,u))})),o.get(`${s}/flows`,...i,D(async(u,p)=>{K(p);try{p.json(await vt(t.flowAdapter))}catch(f){let h=J(f);if(h){p.status(h.statusCode).json({...h.body,hint:"Retry after the owner fixes the plugin runtime."});return}B(p,502,f instanceof Error?f.message:String(f),"upstream_error")}})),o.get(`${s}/flows/:id`,...i,D(async(u,p)=>{K(p),await ts(t.flowAdapter,"get")(u,p)})),o.get(`${s}/nodes/:id`,...i,D(async(u,p)=>{K(p),await ns(t.flowAdapter,"get")(u,p)})),o.get(`${s}/nodes/:id/field/:name`,...i,D(async(u,p)=>{K(p),await rs(t.flowAdapter,"get")(u,p)})),o.get(`${s}/search`,...i,D(async(u,p)=>{K(p),await os(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,D(async(u,p)=>{K(p),await ss(d)(u,p)})),o.get(`${s}/node-types/:type`,...i,D(async(u,p)=>{K(p),await is(d)(u,p)})),o.get(`${s}/palette/search`,...i,D(async(u,p)=>{K(p),await as(d)(u,p)}));let l=e.settings.httpAdminRoot;jm(o,s,i,t,l),o.get(`${s}/propose`,...i,D(Rl(t.proposalStore,t.auditLog,t.flowAdapter,l,t.nodeTypesAdapter))),qm(o,s,i,n,t,l);let c=vl();return o.all(`${s}/links`,c),o.all(`${s}/links/:id`,c),o.use("/mcp/a",bl()),n}a(Ul,"registerGetLinkRoutes");function jm(e,t,n,r,o){e.get(`${t}/proposals`,...n,D(async(s,i)=>{K(i);let d=s.accessLink;if(!d){B(i,401,"Access link required","access_link_missing");return}let l=Z(s,o),c=ve(s.query),u=Oo(s.query),p=await dt(r.flowAdapter),f=p?ze(r.proposalStore,p).expired:se(r.proposalStore),h=Dn(r.proposalStore,d,l,u);i.json({proposals:h.proposals.map(m=>be(r.proposalStore,d,m,{...l,include:c},{includeConflicts:!1})),...rn({...h,returned:h.proposals.length}),...me(f)})})),e.get(`${t}/proposals/:id`,...n,D(async(s,i)=>{K(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){B(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){B(i,401,"Access link required","access_link_missing");return}try{let c=Z(s,o),u=ve(s.query),p=await dt(r.flowAdapter),f=bt(r.proposalStore,l,d,c);i.json(be(r.proposalStore,l,f,{...c,include:u},{...p?{live:p}:{}}))}catch(c){let u=J(c,"get");if(u){i.status(u.statusCode).json({...u.body,hint:u.body.hint??"List proposals with GET \u2026/proposals."});return}z(i,c);return}})),Um(e,t,n,r,o),Bm(e,t,n,r,o)}a(jm,"registerGetLinkProposalRoutes");function Um(e,t,n,r,o){e.get(`${t}/proposals/:id/withdraw`,...n,D((s,i)=>{K(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){B(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){B(i,401,"Access link required","access_link_missing");return}let c=s.query.confirm;if(c!=="1"&&c!=="true"){B(i,400,"Withdraw over GET requires confirm=1","confirm_required",Ae("confirm_required","get"));return}let u=typeof s.query.reason=="string"?s.query.reason:void 0,p=Z(s,o),f=ve(s.query);try{let h=Hn(r.proposalStore,r.auditLog,l,d,u,p);i.json(be(r.proposalStore,l,h,{...p,include:f}))}catch(h){let m=J(h,"get");if(m){let g=m.body.code;B(i,m.statusCode,ut[g]??"Request failed",g,Ae(g,"get"));return}B(i,500,"Internal server error","internal_error");return}}))}a(Um,"registerGetLinkWithdrawRoute");function Bm(e,t,n,r,o){e.get(`${t}/proposals/:id/diff`,...n,D(async(s,i)=>{K(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){B(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){B(i,401,"Access link required","access_link_missing");return}try{let c=bt(r.proposalStore,l,d,Z(s,o)),u=j(s.query,"format"),p=zn(u==="json"||u==="md"?void 0:u);if(!p.ok){B(i,400,"Invalid diff format","invalid_diff_format","Valid formats: summary, full (default summary).");return}i.json(Gn(await Yn(c,r.flowAdapter,r.proposalStore.get(d)),p.format))}catch(c){let u=J(c);if(u){i.status(u.statusCode).json(u.body);return}z(i,c);return}}))}a(Bm,"registerGetLinkDiffRoute");function qm(e,t,n,r,o,s){e.get(`${t}/draft/new`,...n,D(Al(r,o.flowAdapter))),e.get(`${t}/draft/:draftId/append`,...n,D(xl(r))),e.get(`${t}/draft/:draftId/submit`,...n,D(Sl(r,o.proposalStore,o.auditLog,o.flowAdapter,s,o.nodeTypesAdapter)))}a(qm,"registerGetLinkDraftRoutes");y();de();y();var Bl=require("module");function Wm(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Bl.createRequire)(e)}a(Wm,"createPluginRequire");function zm(e){try{let r=Wm()("@node-red/runtime")._?.adminApi?.httpAdmin;if(r&&typeof r.use=="function"&&r!==e.httpAdmin)return r}catch{}return null}a(zm,"resolveEditorHttpAdmin");function ql(e){let t=[e.httpAdmin],n=zm(e);return n&&t.push(n),t}a(ql,"httpAdminAppsForMcpErrors");var Wl=Symbol.for("com55.ai-access.mcpJsonErrorHandler");function Gm(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(Gm,"mcpRpcMatcher");function Wi(e){return e.includes("/mcp")}a(Wi,"isMcpPath");function Km(e){return e.path||e.url||e.originalUrl||""}a(Km,"requestPath");function Jm(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(Jm,"isJsonParseError");function Vm(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(Vm,"isPayloadTooLarge");function Ym(e){let t=Gm(e);return(n,r,o,s)=>{if(o.headersSent){s(n);return}let i=Km(r);if(Jm(n)&&Wi(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(Vm(n)&&Wi(i)){o.status(413).json({error:`Request body exceeds limit of ${Y} bytes`,code:"payload_too_large"});return}if(Wi(i)){if(t(i)){o.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:Wo(n).error,data:{code:"internal_error"}},id:null});return}o.status(500).json(Wo(n));return}s(n)}}a(Ym,"mcpJsonErrorHandler");function Zm(e,t){let n=e;n[Wl]||(n[Wl]=!0,e.use(Ym(t)))}a(Zm,"attachOnce");function zl(e){for(let t of ql(e))Zm(t,e.settings.httpAdminRoot)}a(zl,"attachMcpJsonErrorHandlers");y();y();y();Fr();var fn=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"}},hn=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"}},Dr=class extends Error{static{a(this,"AccessLinkValidationError")}code="access_link_invalid";statusCode=400;constructor(t){super(t),this.name="AccessLinkValidationError"}},jt=class extends Error{static{a(this,"AccessLinkTtlError")}code="invalid_ttl";statusCode=400;hint=on;constructor(t){super(t),this.name="AccessLinkTtlError"}};y();var mn=require("crypto");Fr();function zi(e){return e==="get_link"||e==="rest"||e==="mcp"}a(zi,"isAccessLinkKind");function Gi(e){return e??"get_link"}a(Gi,"resolveAccessLinkKind");function Gl(e){return(0,mn.createHash)("sha256").update(e,"utf8").digest("hex")}a(Gl,"hashToken");function Xm(e,t){let n=Buffer.from(e,"utf8"),r=Buffer.from(t,"utf8");return n.length!==r.length?!1:(0,mn.timingSafeEqual)(n,r)}a(Xm,"hashesEqual");function Hr(e){let t={id:e.id,kind:Gi(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(Hr,"toPublic");var gn=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,mn.randomBytes)(12).toString("hex")),this.randomToken=t.randomToken??(()=>`aia_${(0,mn.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(Hr)}create(t={}){let n=this.now();if([...this.links.values()].filter(p=>this.isActive(p,n)).length>=this.maxActiveLinks)throw new fn(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 jt("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=Gi(t.kind),d=Ci(i);if(o>d)throw new jt(Ei(i,d));let l=this.randomId(),c=this.randomToken(),u={id:l,tokenHash:Gl(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),{...Hr(u),token:c}}revoke(t){let n=this.links.get(t);if(!n)throw new hn(t);return n.revokedAtMs??=this.now(),Hr(n)}getById(t){if(typeof t!="string"||t.length===0)return null;let n=this.links.get(t);return n?Hr(n):null}validateResult(t){if(typeof t!="string"||t.length===0)return{status:"invalid"};let n=Gl(t),r=this.now();for(let o of this.links.values())if(Xm(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:Hr(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)}};Fr();Re();Te();function Ki(e,t){return t}a(Ki,"storeForRequest");function Jl(e){let t=typeof e.user?.username=="string"?e.user.username:void 0;return t?{kind:"admin",username:t}:{kind:"admin"}}a(Jl,"adminActor");function hs(e){let t=Me(e.settings);return new gn({defaultTtlSeconds:t.defaultLinkTtlSeconds,maxActiveLinks:t.maxActiveLinks})}a(hs,"createConfiguredLinkStore");function Vl(e){e.setHeader("Cache-Control","no-store"),e.setHeader("Pragma","no-cache")}a(Vl,"noStore");function Yl(e,t){return(n,r)=>{let s=Ki(e,t).list();Vl(r),r.json({links:s})}}a(Yl,"listLinksHandler");function Zl(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:on});return}i.ttlSeconds=s.ttlSeconds}if(typeof s.label=="string"&&(i.label=s.label),s.kind!==void 0){if(!zi(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=Ki(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",Jl(r),{linkId:d.id,label:d.label,expiresAt:d.expiresAt,createdAt:d.createdAt,kind:l,...p!==void 0?{ttlSeconds:p}:{}})}if(Vl(o),l==="get_link"){let c=Ia(r,e.settings.httpAdminRoot,d.token);o.status(201).json({...d,url:c});return}o.status(201).json({...d})}catch(d){if(d instanceof fn){o.status(d.statusCode).json({error:d.message,code:d.code});return}if(d instanceof jt){o.status(d.statusCode).json({error:d.message,code:d.code,hint:d.hint});return}if(d instanceof Dr){o.status(d.statusCode).json({error:d.message,code:d.code});return}z(o,d);return}}}a(Zl,"createLinkHandler");function Xl(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=Ki(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",Jl(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 hn){o.status(i.statusCode).json({error:i.message,code:i.code});return}z(o,i);return}}}a(Xl,"revokeLinkHandler");y();function Qm(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(Qm,"ttlSecondsFromLink");function eg(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=Qm(l);c!==void 0&&(n.ttlSeconds=c)}return{...e,details:n}}a(eg,"enrichAuditLogEntry");function Ql(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=>eg(d,t));r.json({logs:i})}}a(Ql,"listLogsHandler");y();var Vi=require("@modelcontextprotocol/sdk/server/mcp.js"),ac=require("@modelcontextprotocol/sdk/server/streamableHttp.js"),E=require("zod");tt();de();Gt();le();ct();Zt();en();qe();lt();y();var ec=require("async_hooks");Ne();var tg="ai.redacted",tc=new ec.AsyncLocalStorage;function nc(e,t,n,r){if(!(r<=0))try{e.append(tg,t,{redactions:r,operation:n})}catch{}}a(nc,"appendRedactionAudit");function rc(e){let t=tc.getStore();if(!t)return;let n=_o(e);n>0&&t.record(n)}a(rc,"noteResponseRedactions");function oc(e,t,n,r){return tc.run({record:a(o=>nc(e,t,n,o),"record")},r)}a(oc,"runWithRedactionAudit");function sc(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(sc,"mcpOperationName");function Ji(e){let t=e.accessLink;if(t)return{kind:"access-link",linkId:t.id,...t.label!==void 0?{label:t.label}:{}}}a(Ji,"accessLinkAuditActor");function ic(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=Ji(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"}`;nc(e,i,c,_o(d))}return o(s)}),r()}}a(ic,"redactionAuditMiddleware");Ne();function dc(){return"0.2.0-beta.9"}a(dc,"pluginVersion");function pe(e){let t=JSON.stringify(e);return rc(t),{content:[{type:"text",text:t}]}}a(pe,"jsonResult");function Yi(e){return e===!0?new Set(["payload","baseFlow"]):new Set}a(Yi,"includeSetFromPayloadFlag");function fe(e,t=500){let n=Nr(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(fe,"jsonError");var ng=new Set(["list_proposals","list_my_proposals"]),rg=new Set(["status","limit","offset","mine","flowId"]);function og(e){return!!e&&typeof e=="object"&&typeof e.safeParseAsync=="function"}a(og,"isZodLikeSchema");function sg(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(ng.has(e)&&rg.has(s))return Mn(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 x(d,"invalid_arguments",{details:{parameter:s,errors:i,hint:`Fix '${s}' to match the ${e} input schema (see tools/list).`}})}a(sg,"mcpArgumentError");function lc(e){let t=e,n=t.validateToolInput;typeof n=="function"&&(t.validateToolInput=async(r,o,s)=>{let i=r.inputSchema;if(!og(i))return n.call(e,r,o,s);let d=await i.safeParseAsync(o??{});if(d.success)return d.data;let l=Nr(sg(s,o,d.error.issues),"mcp"),c=l?{...l.body,statusCode:l.statusCode}:{};throw new Error(JSON.stringify(c))})}a(lc,"installMcpArgumentErrorMapping");function cc(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 pe(Zn(t,r))}catch(o){return fe(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 pe(await vt(n.flowAdapter))}catch(o){return fe(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:E.z.string().min(1).describe("Flow / tab id"),summary:E.z.boolean().optional().describe("When true, return node list with sizes only (no field contents)")}},async({id:o,summary:s})=>{try{return pe(s?await Vo(n.flowAdapter,o):await Ge(n.flowAdapter,o))}catch(i){return fe(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:E.z.string().min(1).describe("Node id"),flow:E.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,flow:s})=>{try{return pe(await Yo(n.flowAdapter,o,s))}catch(i){return fe(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:E.z.string().min(1).describe("Node id"),field:E.z.string().min(1).describe("Field name (e.g. template, func)"),lines:E.z.string().optional().describe("Optional 1-based line range A or A-B"),flow:E.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,field:s,lines:i,flow:d})=>{try{return pe(await Zo(n.flowAdapter,o,s,{...d?{flowId:d}:{},...i?{lines:i}:{}}))}catch(l){return fe(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:E.z.string().describe("Substring to find (case-sensitive; non-empty required)"),flow:E.z.string().min(1).optional().describe("Optional tab id")}},async({q:o,flow:s})=>{try{return pe(await Xo(n.flowAdapter,o,s?{flowId:s}:{}))}catch(i){return fe(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:E.z.string().optional().describe("Optional case-insensitive filter")}},async({q:o})=>{try{return pe(await bo(n.nodeTypesAdapter,o?{q:o}:{}))}catch(s){return fe(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:E.z.string().min(1).describe("Node type id (e.g. inject, template) \u2014 case-sensitive"),includeHelp:E.z.boolean().optional().describe("When false, omit help sections (default true)")}},async({type:o,includeHelp:s})=>{try{return pe(await Yt(n.nodeTypesAdapter,o,{includeHelp:s!==!1}))}catch(i){return fe(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:E.z.string().describe("Search string (id / description / keywords; non-empty required)")}},async({q:o})=>{try{return pe(await vo(n.nodeTypesAdapter,o,{...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{}}))}catch(s){return fe(s,502)}})}a(cc,"registerReadTools");function uc(e,t,n,r,o){let s=Z(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 ${Y} 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:E.z.literal("replace_flow").describe("Must be 'replace_flow'"),expectedRev:E.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:E.z.string().min(1).optional().describe("Alias for expectedRev (snake_case); prefer expectedRev"),title:E.z.string().max(80).optional().describe("Optional short human title (\u226480 chars). Auto-generated from changes when omitted."),include_payload:E.z.boolean().optional().describe("When true, include payload and baseFlow in the response (default lean)"),nodes:E.z.array(E.z.record(E.z.string(),E.z.unknown())).optional().describe("Optional node ops (set/add/remove/replace_text). Same as payload.nodes when flow is omitted."),payload:E.z.object({flowId:E.z.string().min(1).describe("Tab id to replace"),flow:E.z.object({id:E.z.string().optional(),type:E.z.string().optional().describe("Usually 'tab' (included by get_flow)"),label:E.z.string().optional(),nodes:E.z.array(E.z.record(E.z.string(),E.z.unknown())).optional().describe("Child nodes for the tab (from get_flow.nodes)"),configs:E.z.array(E.z.record(E.z.string(),E.z.unknown())).optional()}).passthrough().optional().describe("Full tab from get_flow; omit when using nodes ops"),nodes:E.z.array(E.z.record(E.z.string(),E.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 Ot(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={...ue(d,{...s,include:Yi(i.include_payload)}),mine:!0},c=se(n.proposalStore),u=at(it(n.proposalStore,n.proposalStore.get(d.id),o.id));return pe({...l,...u,...me(c)})}catch(d){return fe(d)}}),ag(e,n,o,s),dg(e,n,o,s)}a(uc,"registerProposalTools");function ig(e,t,n,r){let o={mine:E.z.boolean().optional().describe("true: only proposals created by this access link; false: only other links\u2019 proposals"),status:E.z.union([E.z.string(),E.z.array(E.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:E.z.string().optional().describe("Filter by flow tab id (payload.flowId)"),limit:E.z.number().int().min(1).max($n).optional().describe(`Page size (1\u2013${$n}). Omit to return all matches.`),offset:E.z.number().int().min(0).optional().describe("Skip this many filtered results (default 0)"),include_payload:E.z.boolean().optional().describe("When true, include payload and baseFlow on each proposal")},s=a(async i=>{try{let d=Yi(i.include_payload),l=await dt(t.flowAdapter),c=l?ze(t.proposalStore,l).expired:se(t.proposalStore),u={};typeof i.mine=="boolean"&&(u.mine=i.mine);let p=Ti(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=Dn(t.proposalStore,n,r,u);return pe({proposals:f.proposals.map(h=>be(t.proposalStore,n,h,{...r,include:d},{includeConflicts:!1})),...rn({...f,returned:f.proposals.length}),...me(c)})}catch(d){return fe(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(ig,"registerListProposalsTools");function ag(e,t,n,r){ig(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:E.z.string().min(1).describe("Proposal id (prop-\u2026)"),include_payload:E.z.boolean().optional().describe("When true, include payload and baseFlow")}},async({id:o,include_payload:s})=>{try{let i=await dt(t.flowAdapter),d=bt(t.proposalStore,n,o,r);return pe(be(t.proposalStore,n,d,{...r,include:Yi(s)},{...i?{live:i}:{}}))}catch(i){return fe(i)}})}a(ag,"registerProposalQueryTools");function dg(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:E.z.string().min(1).describe("Proposal id (prop-\u2026)"),format:E.z.enum(["summary","full"]).optional().describe("Diff payload format (default summary)")}},async({id:o,format:s})=>{try{let i=bt(t.proposalStore,n,o),d=zn(s??"summary");if(!d.ok)return fe(new x("Invalid diff format","invalid_diff_format",{details:{hint:"Valid formats: summary, full (default summary)."}}));let l=Gn(await Yn(i,t.flowAdapter,t.proposalStore.get(o)),d.format);return pe(l)}catch(i){return fe(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:E.z.string().min(1).describe("Your open proposal id (prop-\u2026)"),nodeId:E.z.string().min(1).optional().describe("Conflict nodeId from rebase.conflicts; use the flow id for tab/env conflicts. Required with choice."),choice:E.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:E.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:E.z.string().max(80).optional().describe("Optional replacement title (body replacement mode only)"),payload:E.z.object({flowId:E.z.string().min(1).describe("Target flow id; must remain the proposal\u2019s flow id"),flow:E.z.record(E.z.string(),E.z.unknown()).optional().describe("Full replacement tab based on expectedRev; preserve all desired edits. Use either flow or nodes."),nodes:E.z.array(E.z.record(E.z.string(),E.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 $o(t.proposalStore,t.auditLog,n,o,s,t.flowAdapter,r,t.nodeTypesAdapter);return pe(be(t.proposalStore,n,i,r))}catch(i){return fe(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:E.z.string().min(1).describe("Proposal id (prop-\u2026) owned by this access link"),reason:E.z.string().optional().describe("Optional withdraw reason")}},({id:o,reason:s})=>{try{let i=Hn(t.proposalStore,t.auditLog,n,o,s,r);return pe(be(t.proposalStore,n,i,r))}catch(i){return fe(i)}})}a(dg,"registerProposalActionTools");function lg(e,t,n){let r=new Vi.McpServer({name:"node-red-ai-access",version:dc()});lc(r);let o=n.accessLink;if(!o)throw new Error("Access link required");return cc(r,e,t,n),uc(r,e,t,n,o),r}a(lg,"createAiMcpServer");function cg(e){let t=Ji(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:fo(n,r),label:"oauth"}}a(cg,"mcpAuditActor");function Zi(e,t){return async(n,r,o)=>{try{if(!n.accessLink&&!n.auth){r.setHeader("WWW-Authenticate",ge),r.status(401).json({error:He,code:ht});return}let s={...t,flowAdapter:Lt(t.flowAdapter)},i=n.accessLink?lg(e,s,n):ug(e,s,n),d=new ac.StreamableHTTPServerTransport({enableJsonResponse:!0});await i.connect(d),await oc(t.auditLog,cg(n),sc(n.body),()=>d.handleRequest(n,r,n.body)),r.on("close",()=>{d.close(),i.close()})}catch(s){if(!r.headersSent){z(r,s);return}o(s instanceof Error?s:new Error(String(s)))}}}a(Zi,"mcpRpcPostHandler");function ug(e,t,n){let r=new Vi.McpServer({name:"node-red-ai-access",version:dc()});if(lc(r),cc(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:fo(i,s),kind:"mcp",createdAt:new Date().toISOString(),expiresAt:new Date((n.auth?.expiresAt??0)*1e3).toISOString(),createdBy:s,oauthClientId:i,oauthUser:s};uc(r,e,t,n,l)}return r}a(ug,"createOAuthMcpServer");function jr(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(jr,"mcpRpcMethodNotAllowed");y();var Ur=require("crypto"),Qn=require("fs"),ta=require("path"),ne=require("@modelcontextprotocol/sdk/server/auth/errors.js"),gc=require("@modelcontextprotocol/sdk/server/auth/handlers/authorize.js"),na=require("@modelcontextprotocol/sdk/server/auth/handlers/metadata.js"),yc=require("@modelcontextprotocol/sdk/server/auth/handlers/register.js"),wc=require("@modelcontextprotocol/sdk/server/auth/handlers/revoke.js"),bc=require("@modelcontextprotocol/sdk/server/auth/handlers/token.js"),vc=require("@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js"),kc=require("@modelcontextprotocol/sdk/server/auth/router.js"),Ac=rr(require("express"),1);de();Ye();y();function pt(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t]??t)}a(pt,"esc");var pc={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>'},pg={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."]},Xi='<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>',fg=`
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 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=`
1026
+ `;function fc(e){let t=e.scopes.map(n=>{let[r,o]=pg[n]??[n,""];return`<li data-scope="${pt(n)}">${pc[n]??pc.read}<div><b>${pt(r)}</b><span>${pt(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>${fg}</style></head><body><main class="card" data-request="${pt(e.requestId)}" data-token-key="${pt(e.tokenKey)}" data-admin-root="${pt(e.adminRoot)}"><div class="head">${Xi}<div class="brand"><b>Node-RED AI Access</b><span>Authorization request</span></div></div><div class="body"><h1><span class="app">${pt(e.redirectHost)}</span> wants to access your Node-RED</h1><p class="sub">Access tokens will be sent to <span class="host">${pt(e.redirectHost)}</span>. App name (provided by the client, unverified): <b>${pt(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="${pt(e.nonce)}">${e.script}</script></body></html>`}a(fc,"renderConsentPage");y();y();var ms=`
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 tc=`(function () {
1053
- ${as}
1052
+ `;var hc=`(function () {
1053
+ ${ms}
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 @@ ${as}
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,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`
1177
+ })();`;Te();var Ut=a(e=>(0,Ur.createHash)("sha256").update(e).digest("hex"),"hash"),wn=a(()=>(0,Ur.randomBytes)(32).toString("base64url"),"secret"),hg=36e5,mg=25,gg=5,yg=500,mc=a(()=>({clients:[],codes:[],tokens:[]}),"emptyDb"),gs=class{constructor(t,n){this.maxClients=n;this.path=(0,ta.join)(t.root,"oauth.json");try{let r=(0,Qn.existsSync)(this.path)?JSON.parse((0,Qn.readFileSync)(this.path,"utf8")):mc();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=mc()}(0,Qn.mkdirSync)(t.root,{recursive:!0,mode:448}),this.prune()}static{a(this,"OAuthStore")}db;path;save(){bn((0,ta.join)(this.path,".."),this.path,St(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-hg).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 ne.InvalidClientMetadataError("Maximum active OAuth clients reached");if(this.db.clients.length-this.approvedCount()>=mg)throw new ne.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 ne.InvalidClientMetadataError("Redirect URIs must use HTTPS or loopback HTTP");if(t.token_endpoint_auth_method&&t.token_endpoint_auth_method!=="none")throw new ne.InvalidClientMetadataError("Only public clients are supported");let n={...t,client_secret:void 0,client_secret_expires_at:void 0,client_id:wn(),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 ne.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 oa(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(oa,"describeOAuthRouteCollision");var sa=a((e,t)=>e.toLowerCase().startsWith(t.toLowerCase()),"startsWithCi");function Qi(e){return e===!1?void 0:`/${typeof e=="string"?e:"/"}`.replace(/\/+/g,"/").replace(/\/$/,"")}a(Qi,"normalizeRoot");function ea(e,t){if(e!==void 0){if(!e)return t;if(sa(t,`${e}/`))return t.slice(e.length)}}a(ea,"relativePrefix");function wg(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(wg,"makeLiveCheck");function bg(e,t){if(!e||!t.length)return[];let n=[];try{e(s=>n.push(s))}catch{return[]}let r=wg(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=>sa(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(bg,"findHttpInCollisions");var vg=new Set(["bound dispatch","handle"]),kg=["authorize","token","register","revoke"],Ag=["authorization-server","protected-resource"];function xg(e){let t=e.length;for(;t>0&&e.charCodeAt(t-1)===47;)t--;return e.slice(0,t)}a(xg,"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 xc(e){let t=Qi(e.httpAdminRoot),n=e.httpNode?Qi(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=ea(t,p),h=ea(n,p),m=h?bg(e.eachNode,[h]):[],g=new Set(m.map(k=>k.path.toLowerCase())),w=h?ra(e.httpNode,[h]).filter(k=>!(k.source==="route"&&vg.has(k.layer??"")&&g.has(k.path.toLowerCase()))):[],v=f?ra(e.httpAdmin,[f]):[];return Sg([...v,...w,...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=Qi(h.root),g=xg(h.path);for(let w of p){let v=ea(m,w);!v||!l(`${g}${v}`)||f.push({path:w,source:"httpStatic",file:`${g}${v}`})}}},"checkStatic");return u(kg.map(p=>`${r}${p}`),i),u(Ag.map(p=>`${o}${p}`),d),{authorization:i,metadata:d}}a(xc,"scanOAuthRouteCollisions");function Rg(e){let t=a(n=>{try{return e?.[n]}catch{return}},"read");return t("_router")?.stack??t("router")?.stack??[]}a(Rg,"routerStack");function Tg(e){let t=e.route?.path;return t?(Array.isArray(t)?t:[t]).map(String):[]}a(Tg,"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 ra(e,t=["/.well-known/oauth-","/mcp/oauth/"]){let n=[];for(let r of Rg(e)){if(r.handle?.oauthOwned)continue;let o=Tg(r);for(let s of o)t.some(i=>sa(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(...ra({router:{stack:r.handle.stack}},t))}return n}a(ra,"detectOAuthRouteCollisionDetails");var ys=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 ne.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(Ut(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&&Bt(s.href)!==i.resource)throw new ne.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===Ut(n)&&l.type==="refresh"&&l.clientId===t.client_id);if(!s||s.expires<=Date.now())throw new ne.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 ne.InvalidGrantError("Refresh token reuse detected");let i=this.refreshMax()*1e3,d=s.familyStart??Date.now();if(i>0&&d+i<=Date.now())throw new ne.InvalidGrantError("Refresh token family reached its maximum lifetime");if(o&&Bt(o.href)!==s.resource)throw new ne.InvalidGrantError("Resource mismatch");if(r?.some(l=>!s.scopes.includes(l)))throw new ne.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===Ut(t)&&o.type==="access"&&o.expires>Date.now());if(!n||n.resource!==Bt(this.resource()))throw new ne.InvalidTokenError("Invalid access token");let r=this.clientsStore.data.clients.find(o=>o.client_id===n.clientId);if(!r)throw new ne.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===Ut(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=wn();return this.clientsStore.data.codes.push({hash:Ut(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=Bt(this.resource());if(t&&Bt(t.href)!==n)throw new ne.InvalidGrantError("Resource mismatch");return n}findCode(t,n){let r=this.clientsStore.data.codes.find(o=>o.hash===Ut(n)&&o.clientId===t.client_id);if(!r||r.expires<=Date.now())throw new ne.InvalidGrantError("Invalid or expired authorization code");return r}issue(t,n,r,o,s=wn(),i=Date.now()){let d=wn(),l=wn(),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:Ut(d),type:"access",clientId:t,family:s,scopes:n,resource:r,expires:c+this.accessTtl()*1e3,user:o,rotated:!1},{hash:Ut(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 Bt(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(Bt,"canonicalResource");var Xn=["read","propose"];function _g(e){let t=(e??"/").split("/").filter(Boolean).join("/");return t?`/${t}/`:"/"}a(_g,"consentAdminRoot");var Cg=new Set(["localhost","127.0.0.1","[::1]"]);function yn(e){return typeof e=="string"?e:""}a(yn,"value");function Sc(e,t,n,r){let o=a(T=>`${n().replace(/\/$/,"")}${T}`,"abs"),s=a((T,S,F,N)=>{let P=S.url;S.url="/",T(S,F,A=>{S.url=P,N(A)})},"atRoot"),i=new Map,d=a(()=>{let T=Date.now();for(let[S,F]of i)F.expires<=T&&i.delete(S)},"sweep"),l=a(T=>{d();let S=[...i].filter(([,F])=>F.clientId===T);for(;S.length>=gg;)i.delete(S.shift()[0]);return i.size<yg},"reservePending"),c=a((T,S)=>{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'; ${S?`script-src 'nonce-${S}'; `:""}style-src 'unsafe-inline'; connect-src 'self'; form-action 'none'; base-uri 'none'; frame-ancestors 'none'`)},"securityHeaders"),u=so(e.settings.httpAdminRoot),p=a(T=>{if(!ae(e.settings))return[...Xn];let S=Tt(T);return S==="*"?[...Xn]:S==="read"?["read"]:[]},"grantableFor"),f=a(T=>{let S=T.get("sec-fetch-site");if(S&&S!=="same-origin")return!1;let F=T.get("origin");if(!F)return!0;let N=new Set;try{N.add(new URL(n()).origin)}catch{}let P=T.get("host");return P&&(N.add(`http://${P}`),N.add(`https://${P}`)),N.has(F)},"sameOrigin"),h=a((T,S)=>{d();let F=T.query,N=t.clientsStore.getClient(yn(F.client_id));if(!N){S.status(400).send("Unknown OAuth client");return}let P=yn(F.redirect_uri)||(N.redirect_uris.length===1?N.redirect_uris[0]:"");if(!N.redirect_uris.some(ee=>(0,gc.redirectUriMatches)(P,ee))){S.status(400).send("Unregistered redirect URI");return}if(F.response_type!=="code"||F.code_challenge_method!=="S256"||!/^[A-Za-z0-9_-]{43,128}$/.test(yn(F.code_challenge))){S.status(400).send("Authorization code with PKCE S256 is required");return}let A=[...new Set((yn(F.scope)||"read").split(" ").filter(Boolean))];if(A.some(ee=>!Xn.includes(ee))){S.status(400).send("Unsupported scope");return}let I=yn(F.resource)||r(),b;try{b=Bt(I)}catch{S.status(400).send("Invalid resource");return}if(b!==Bt(r())){S.status(400).send("Resource mismatch");return}if(!l(N.client_id)){S.status(429).send("Too many pending authorization requests; try again later");return}let M=wn();i.set(M,{clientId:N.client_id,redirect:P,challenge:yn(F.code_challenge),scopes:A,resource:I,state:yn(F.state),csrf:wn(),expires:Date.now()+10*6e4});let H=new URL(P).host,L=(0,Ur.randomBytes)(16).toString("base64");c(S,L),S.status(200).type("html").send(fc({requestId:M,tokenKey:u,adminRoot:_g(e.settings.httpAdminRoot),nonce:L,redirectHost:H,loopbackRedirect:Cg.has(new URL(P).hostname),clientName:N.client_name||"Unnamed client",scopes:A,script:hc}))},"authorizePage"),m=a((T,S,F)=>{c(S),Ac.default.json({limit:"4kb"})(T,S,()=>Ce(e,"api")(T,S,()=>{try{d();let N=/^\/mcp\/oauth\/consent\/([A-Za-z0-9_-]+)\/?$/.exec(T.path)?.[1]??"",P=i.get(N),A=P&&t.clientsStore.getClient(P.clientId);if(!P||!A){S.status(404).json({error:"Consent request expired or unknown"});return}let I=ae(e.settings)&&T.user?.username||"owner",b=P.scopes.filter(L=>p(T).includes(L));if(!b.length){S.status(403).json({error:"Your Node-RED permissions do not allow granting the requested scopes",grantable:b});return}if(T.method==="GET"){P.approver=I,S.json({client_name:A.client_name??null,redirect_host:new URL(P.redirect).host,requested:P.scopes,grantable:b,user:I,csrf:P.csrf});return}if(T.method!=="POST"){S.status(405).end();return}let M=T.body??{};if(!f(T)||M.csrf!==P.csrf||P.approver!==I){S.status(403).json({error:"Consent request verification failed"});return}i.delete(N);let H=new URL(P.redirect);if(M.decision!=="allow")H.searchParams.set("error","access_denied");else{try{t.clientsStore.approve(A.client_id)}catch{S.status(409).json({error:"Maximum active OAuth clients reached"});return}let L=t.createCode(A,{redirectUri:P.redirect,codeChallenge:P.challenge,scopes:b,resource:new URL(P.resource)},I);H.searchParams.set("code",L)}P.state&&H.searchParams.set("state",P.state),S.json({redirect:H.href})}catch(N){F(N)}}))},"consentApi"),g=(0,yc.clientRegistrationHandler)({clientsStore:t.clientsStore,rateLimit:{windowMs:36e5,max:10,standardHeaders:!0,legacyHeaders:!1}}),w=(0,bc.tokenHandler)({provider:t}),v=(0,wc.revocationHandler)({provider:t}),k,$="";return{server:a((T,S,F)=>{let N=T.path,P=N.replace(/\/+$/,"").toLowerCase();if(P==="/mcp/oauth/authorize"){T.method==="GET"?h(T,S,F):S.status(405).set("Allow","GET").end();return}if(P.startsWith("/mcp/oauth/consent/")){m(T,S,F);return}if(!["/mcp/oauth/token","/mcp/oauth/register","/mcp/oauth/revoke"].includes(P)){F();return}let A=n();if(A.startsWith("https://")||/^http:\/\/(localhost|127\.0\.0\.1)(:|\/|$)/.test(A)){if($!==A)try{k=(0,kc.mcpAuthRouter)({provider:t,issuerUrl:new URL(A),baseUrl:new URL(`${A.replace(/\/$/,"")}/mcp/oauth/`),resourceServerUrl:new URL(r()),scopesSupported:Xn,clientRegistrationOptions:{rateLimit:{windowMs:36e5,max:10}}}),$=A}catch{k=void 0}if(k){let I=T.url;T.url=T.url.slice(10)||"/",k(T,S,b=>{T.url=I,F(b)});return}}if(N==="/mcp/oauth/token"){s(w,T,S,F);return}if(N==="/mcp/oauth/register"){s(g,T,S,F);return}if(N==="/mcp/oauth/revoke"){s(v,T,S,F);return}F()},"server"),metadata:a((T,S,F)=>{let N=T.path;if(N==="/.well-known/oauth-authorization-server"){s((0,na.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:Xn}),T,S,F);return}if(N==="/.well-known/oauth-protected-resource/mcp"||N==="/.well-known/oauth-protected-resource"){s((0,na.metadataHandler)({resource:Bt(r()),authorization_servers:[n()],scopes_supported:Xn}),T,S,F);return}F()},"metadata"),auth:a((T,S,F)=>{try{let N=S.set.bind(S);S.set=((P,A)=>(P==="WWW-Authenticate"&&typeof A=="string"&&(A=A.replace('scope="read"','scope="read propose"')),typeof P=="string"?N(P,A??""):N(P))),(0,vc.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,S,P=>{S.set=N,F(P)})}catch(N){F(N)}},"auth")}}a(Sc,"makeOAuthHandlers");y();var Br=require("fs"),er=require("path"),Rc=require("url"),Tc=require("yaml");function Eg(){return typeof __dirname<"u"?__dirname:(0,er.dirname)((0,Rc.fileURLToPath)(R))}a(Eg,"moduleDir");function Pc(){let e=Eg(),t=[(0,er.join)(e,"..","docs","api","openapi.yaml"),(0,er.join)(e,"..","..","docs","api","openapi.yaml"),(0,er.join)(process.cwd(),"docs","api","openapi.yaml")];for(let n of t)if((0,Br.existsSync)(n))return n;return null}a(Pc,"resolveOpenApiYamlPath");function Ig(){let e=Pc();if(!e)throw new Error("OpenAPI specification file was not found");return(0,Br.readFileSync)(e,"utf8")}a(Ig,"loadOpenApiYaml");function _c(){return(e,t)=>{try{let n=Ig();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(_c,"openApiYamlHandler");var ia;function Lg(){let e=Pc();if(!e)throw new Error("OpenAPI specification file was not found");return ia?.path!==e&&(ia={path:e,json:JSON.stringify((0,Tc.parse)((0,Br.readFileSync)(e,"utf8")))}),ia.json}a(Lg,"loadOpenApiJson");function Cc(){return(e,t)=>{try{t.status(200).type("application/json").send(Lg())}catch(n){let r=n instanceof Error?n.message:String(n);t.status(500).json({error:r})}}}a(Cc,"openApiJsonHandler");y();y();y();y();function Ng(){return`
1178
1178
  var settingsData = null;
1179
1179
  var settingsSaving = false;
1180
1180
  var settingsTimer = null;
@@ -1312,7 +1312,7 @@ ${as}
1312
1312
  return payload;
1313
1313
  }
1314
1314
 
1315
- `}a(bg,"settingsPartForm");function vg(){return`
1315
+ `}a(Ng,"settingsPartForm");function Fg(){return`
1316
1316
  function secretPatternsEditable() {
1317
1317
  return settingsCanEdit() && !fieldLocked('secretNamePatterns');
1318
1318
  }
@@ -1400,9 +1400,9 @@ ${as}
1400
1400
  });
1401
1401
  }
1402
1402
 
1403
- `}a(vg,"settingsPartSecrets");function kg(){return`
1403
+ `}a(Fg,"settingsPartSecrets");function Og(){return`
1404
1404
  function settingsFlash(msg, ok) {
1405
- var tab = document.querySelector('nav.tabs button[aria-selected="true"]');
1405
+ var tab = document.querySelector('nav.tabs button[aria-current="page"]');
1406
1406
  var target = tab && tab.getAttribute('data-tab') === 'oauth' ? 'oauth-flash' : 'settings-flash';
1407
1407
  if (ok) showToast(msg, true);
1408
1408
  else showFlash($(target), msg, false);
@@ -1502,7 +1502,7 @@ ${as}
1502
1502
  }
1503
1503
  }
1504
1504
 
1505
- `}a(kg,"settingsPartSave");function Ag(){return`
1505
+ `}a(Og,"settingsPartSave");function $g(){return`
1506
1506
  function editorNodeHref(c) {
1507
1507
  var base = editorRoot && editorRoot !== '/' ? editorRoot.replace(/\\/$/, '') + '/' : '/';
1508
1508
  if (c.nodeId) return base + '#node/' + encodeURIComponent(c.nodeId);
@@ -1610,7 +1610,7 @@ ${as}
1610
1610
  }
1611
1611
  }
1612
1612
 
1613
- `}a(Ag,"settingsPartOAuth");function xg(){return`
1613
+ `}a($g,"settingsPartOAuth");function Mg(){return`
1614
1614
  function loadOAuthTab() {
1615
1615
  loadSettings();
1616
1616
  loadOAuthStatus();
@@ -1643,7 +1643,7 @@ ${as}
1643
1643
  showToast('Server URL copied.', true);
1644
1644
  });
1645
1645
  })();
1646
- `}a(xg,"settingsPartWire");function yc(){return bg()+vg()+kg()+Ag()+xg()}a(yc,"pageClientSettingsScript");function Tg(){return`
1646
+ `}a(Mg,"settingsPartWire");function Ec(){return Ng()+Fg()+Og()+$g()+Mg()}a(Ec,"pageClientSettingsScript");function Hg(){return`
1647
1647
  (function () {
1648
1648
  var root = document.getElementById('mcp-root');
1649
1649
  if (!root) return;
@@ -1661,6 +1661,7 @@ ${as}
1661
1661
  var proposalsFilter = 'pending';
1662
1662
  var proposalsSearchQuery = '';
1663
1663
  var cachedProposals = [];
1664
+ var deployUnknownBlocks = [];
1664
1665
  // Decided proposals load in pages (Load more); pending always load in full.
1665
1666
  var DECIDED_PAGE_SIZE = 50;
1666
1667
  var DECIDED_STATUSES = 'approved,rejected,failed,withdrawn,expired,superseded';
@@ -1876,7 +1877,7 @@ ${as}
1876
1877
  }
1877
1878
  });
1878
1879
  }
1879
- ${as}
1880
+ ${ms}
1880
1881
  function isStatusPath(path) {
1881
1882
  return path === '/status' || path.indexOf('/status?') === 0;
1882
1883
  }
@@ -2014,7 +2015,7 @@ ${as}
2014
2015
  }
2015
2016
  return body;
2016
2017
  }
2017
- `}a(Tg,"pageClientScriptPartA");function Rg(){return`
2018
+ `}a(Hg,"pageClientScriptPartA");function jg(){return`
2018
2019
  // Time helpers \u2014 keep in sync with page-time.ts (always browser-local Intl).
2019
2020
  function titleFromDisplayName(displayName) {
2020
2021
  var s = String(displayName || '');
@@ -2121,7 +2122,7 @@ ${as}
2121
2122
  btn.disabled = false;
2122
2123
  }, 1600);
2123
2124
  }
2124
- `}a(Rg,"pageClientScriptPartATimeCopy");function Pg(){return`
2125
+ `}a(jg,"pageClientScriptPartATimeCopy");function Ug(){return`
2125
2126
  function shortId(value, keep) {
2126
2127
  var s = String(value == null ? '' : value);
2127
2128
  var n = keep || 8;
@@ -2191,6 +2192,7 @@ ${as}
2191
2192
  function actionBadge(action) {
2192
2193
  var labels = {
2193
2194
  'proposal.created': 'Created',
2195
+ 'proposal.late_deploy_overwrite': 'Late deploy warning',
2194
2196
  'proposal.approved': 'Approved',
2195
2197
  'proposal.rejected': 'Rejected',
2196
2198
  'proposal.failed': 'Failed',
@@ -2212,6 +2214,7 @@ ${as}
2212
2214
  function actionFilterLabel(action) {
2213
2215
  var labels = {
2214
2216
  'proposal.created': 'Created',
2217
+ 'proposal.late_deploy_overwrite': 'Late deploy warning',
2215
2218
  'proposal.approved': 'Approved',
2216
2219
  'proposal.rejected': 'Rejected',
2217
2220
  'proposal.failed': 'Failed',
@@ -2257,7 +2260,7 @@ ${as}
2257
2260
  }
2258
2261
  return '';
2259
2262
  }
2260
- `}a(Pg,"pageClientScriptPartAFormat");function _g(){return`
2263
+ `}a(Ug,"pageClientScriptPartAFormat");function Bg(){return`
2261
2264
  function lookupCachedProposal(id) {
2262
2265
  if (!id) return null;
2263
2266
  return cachedProposals.filter(function (x) { return x.id === id; })[0] || null;
@@ -2378,7 +2381,7 @@ ${as}
2378
2381
  return k + '=' + String(v);
2379
2382
  }).join(', '));
2380
2383
  }
2381
- `}a(_g,"pageClientScriptPartAAudit");function Cg(){return`
2384
+ `}a(Bg,"pageClientScriptPartAAudit");function qg(){return`
2382
2385
  // Hash helpers \u2014 keep in sync with page-hash.ts
2383
2386
  var TAB_IDS = ['status', 'settings', 'links', 'oauth', 'proposals'];
2384
2387
  function hashForTab(tab) {
@@ -2388,7 +2391,7 @@ ${as}
2388
2391
  }
2389
2392
 
2390
2393
  function tabFromHash(hash) {
2391
- var raw = String(hash || '').replace(/^#/, '');
2394
+ var raw = String(hash || '').replace(/^#/, '').replace(/^section-/, '');
2392
2395
  if (!raw || raw.indexOf('proposal=') === 0) return '';
2393
2396
  if (raw === 'audit' || raw === 'logs') return 'logs';
2394
2397
  if (raw === 'overview') return 'status';
@@ -2431,14 +2434,19 @@ ${as}
2431
2434
  });
2432
2435
  }
2433
2436
 
2437
+ var selectedLocationHash;
2434
2438
  function selectTab(target, options) {
2435
2439
  options = options || {};
2436
2440
  if (!target) return;
2437
2441
  document.querySelectorAll('nav.tabs button').forEach(function (b) {
2438
- b.setAttribute('aria-selected', b.getAttribute('data-tab') === target ? 'true' : 'false');
2442
+ b.setAttribute('aria-current', b.getAttribute('data-tab') === target ? 'page' : 'false');
2439
2443
  });
2440
2444
  document.querySelectorAll('section.panel').forEach(function (sec) {
2441
2445
  sec.classList.toggle('active', sec.id === 'section-' + target);
2446
+ if (options.focusHeading && sec.id === 'section-' + target) {
2447
+ var heading = sec.querySelector('h2');
2448
+ if (heading) heading.focus();
2449
+ }
2442
2450
  });
2443
2451
  if (target !== 'proposals') {
2444
2452
  clearProposalHighlight();
@@ -2450,6 +2458,7 @@ ${as}
2450
2458
  replaceLocationHash('#' + hashForTab(target));
2451
2459
  }
2452
2460
  }
2461
+ selectedLocationHash = String(location.hash || '');
2453
2462
  }
2454
2463
 
2455
2464
  function loadTabData(target) {
@@ -2467,7 +2476,10 @@ ${as}
2467
2476
  document.querySelectorAll('nav.tabs button').forEach(function (btn) {
2468
2477
  btn.addEventListener('click', function () {
2469
2478
  var target = btn.getAttribute('data-tab');
2470
- selectTab(target);
2479
+ if (location.hash !== '#' + hashForTab(target)) {
2480
+ history.pushState(null, '', location.pathname + location.search + '#' + hashForTab(target));
2481
+ }
2482
+ selectTab(target, { skipHash: true });
2471
2483
  loadTabData(target);
2472
2484
  });
2473
2485
  });
@@ -2664,7 +2676,7 @@ ${as}
2664
2676
  }
2665
2677
  }
2666
2678
 
2667
- `}a(Cg,"pageClientScriptPartATabs");function Eg(){return`
2679
+ `}a(qg,"pageClientScriptPartATabs");function Wg(){return`
2668
2680
  async function loadLinks(opts) {
2669
2681
  opts = opts || {};
2670
2682
  if (!aiOn || !canManage) {
@@ -2738,7 +2750,7 @@ ${as}
2738
2750
  showToast(e.message || String(e), false);
2739
2751
  }
2740
2752
  }
2741
- `}a(Eg,"pageClientScriptPartA2");function Ig(){return`
2753
+ `}a(Wg,"pageClientScriptPartA2");function zg(){return`
2742
2754
 
2743
2755
  function normalizeLinkKind(kind) {
2744
2756
  if (kind === 'rest' || kind === 'mcp' || kind === 'get_link') return kind;
@@ -3463,7 +3475,7 @@ ${as}
3463
3475
  decided: Math.max(decidedTotal, cachedProposals.filter(function (p) { return (p.status !== 'pending' && p.status !== 'conflict'); }).length)
3464
3476
  };
3465
3477
  }
3466
- `}a(Ig,"pageClientScriptPartB");function Lg(){return`
3478
+ `}a(zg,"pageClientScriptPartB");function Gg(){return`
3467
3479
  var DIFF_MODE_KEY = 'ai-access-diff-mode';
3468
3480
  var DIFF_SPLIT_MIN_PX = ${900};
3469
3481
  var PRESENCE_SHORT_TEXT_MAX = ${120};
@@ -3580,7 +3592,7 @@ ${as}
3580
3592
  '</div>';
3581
3593
  }
3582
3594
 
3583
- `}a(Lg,"pageClientScriptPartBDiff");function Ng(){return`
3595
+ `}a(Gg,"pageClientScriptPartBDiff");function Kg(){return`
3584
3596
  function isCollapsiblePresenceText(value) {
3585
3597
  var s = String(value == null ? '' : value);
3586
3598
  if (s.indexOf('\\n') >= 0) return true;
@@ -3683,7 +3695,7 @@ ${as}
3683
3695
  });
3684
3696
  });
3685
3697
  }
3686
- `}a(Ng,"pageClientScriptPartBDiffPresence");function Fg(){return`
3698
+ `}a(Kg,"pageClientScriptPartBDiffPresence");function Jg(){return`
3687
3699
  function textChangeLineCount(ch) {
3688
3700
  return (ch.lines || []).length;
3689
3701
  }
@@ -3742,7 +3754,7 @@ ${as}
3742
3754
  '<code class="diff-before" title="before">' + esc(JSON.stringify(ch.before)) + '</code>' +
3743
3755
  ' \u2192 <code class="diff-after" title="after">' + esc(JSON.stringify(ch.after)) + '</code></div>';
3744
3756
  }
3745
- `}a(Fg,"pageClientScriptPartBDiffView");function Og(){return`
3757
+ `}a(Jg,"pageClientScriptPartBDiffView");function Vg(){return`
3746
3758
  function renderDiffSections(sections, mode, emptyMsg) {
3747
3759
  var parts = sections.summary && (sections.summary.tabChanges || []).length
3748
3760
  ? '<p>Flow settings changed: ' + esc(sections.summary.tabChanges.join(', ')) + '</p>' : '';
@@ -3948,7 +3960,7 @@ ${as}
3948
3960
  // Full view kept for mode-toggle re-render inside .proposal-changes.
3949
3961
  return renderHumanDiff(diff, mode);
3950
3962
  }
3951
- `}a(Og,"pageClientScriptPartBDiffView2");function $g(){return`
3963
+ `}a(Vg,"pageClientScriptPartBDiffView2");function Yg(){return`
3952
3964
  function updatePendingBadges() {
3953
3965
  var counts = countPendingBuckets();
3954
3966
  var fullText = formatPendingBadgeText(counts.live, counts.stale);
@@ -3972,6 +3984,16 @@ ${as}
3972
3984
 
3973
3985
  function setProposalsFilter(next) {
3974
3986
  proposalsFilter = next === 'decided' ? 'decided' : 'pending';
3987
+ var blockBanner = document.getElementById('deploy-unknown-blocks');
3988
+ if (!blockBanner && list.parentNode) {
3989
+ blockBanner = document.createElement('div');
3990
+ blockBanner.id = 'deploy-unknown-blocks';
3991
+ list.parentNode.insertBefore(blockBanner, list);
3992
+ }
3993
+ if (blockBanner) {
3994
+ blockBanner.innerHTML = renderDeployUnknownBlocks(deployUnknownBlocks);
3995
+ bindProposalActions(blockBanner);
3996
+ }
3975
3997
  var filters = $('proposals-filters');
3976
3998
  if (filters) {
3977
3999
  filters.querySelectorAll('.filter-chip').forEach(function (chip) {
@@ -4024,6 +4046,23 @@ ${as}
4024
4046
  approveProposal(btn.getAttribute('data-id'));
4025
4047
  });
4026
4048
  });
4049
+ list.querySelectorAll('.confirm-live-checked-btn').forEach(function (btn) {
4050
+ bindOnce(btn, 'click', async function () {
4051
+ openConfirmDialog({
4052
+ title: 'Confirm live flows checked',
4053
+ message: 'Have you checked the live flows? This clears the global approval block, but the earlier deploy may still land.',
4054
+ confirmLabel: 'Confirm live checked',
4055
+ danger: true,
4056
+ onConfirm: async function () {
4057
+ try {
4058
+ await api('/proposals/' + encodeURIComponent(btn.getAttribute('data-id')) + '/confirm-live-checked', { method: 'POST', body: JSON.stringify({ attemptId: btn.getAttribute('data-attempt-id') }) });
4059
+ await loadProposals();
4060
+ loadLogs();
4061
+ } catch (e) { showToast(e.message || String(e), false); }
4062
+ }
4063
+ });
4064
+ });
4065
+ });
4027
4066
  list.querySelectorAll('.reject-btn').forEach(function (btn) {
4028
4067
  bindOnce(btn, 'click', function () {
4029
4068
  openRejectDialog(btn.getAttribute('data-id'));
@@ -4061,7 +4100,7 @@ ${as}
4061
4100
  });
4062
4101
  });
4063
4102
  }
4064
- `}a($g,"pageClientScriptPartB2");function Mg(){return`
4103
+ `}a(Yg,"pageClientScriptPartB2");function Zg(){return`
4065
4104
  function renderDeleteBanner(diff) {
4066
4105
  if (!isLargeDelete(diff)) return '';
4067
4106
  var beforeN = diff.summary.beforeNodeCount || 0;
@@ -4099,7 +4138,7 @@ ${as}
4099
4138
  '</details>';
4100
4139
  }
4101
4140
 
4102
- `}a(Mg,"pageClientScriptPartB2Actions");function Dg(){return`
4141
+ `}a(Zg,"pageClientScriptPartB2Actions");function Xg(){return`
4103
4142
  function renderRequestedVia(p) {
4104
4143
  if (!p.createdBy) return '';
4105
4144
  if (p.createdBy.label) {
@@ -4169,11 +4208,13 @@ ${as}
4169
4208
  // Show for any large delete \u2014 including stale \u2014 so owners see the risk.
4170
4209
  var deleteBanner = cached && !proposalHasNoNetChange(cached) ? renderDeleteBanner(cached) : '';
4171
4210
  // Card-level banner even before diff loads (warnings come from list API).
4211
+ reason += p.deploymentAlert ? '<div role="alert" class="warning">' + esc(p.deploymentAlert) + '</div>' : '';
4212
+ reason += p.rejectedWhileDeployUnknown ? '<div role="alert" class="warning">Rejected while deploy outcome unknown. An earlier deploy may still land.</div>' : '';
4172
4213
  var deployBanner = p.deployPending
4173
4214
  ? '<div class="proposal-warnings" role="status"><p class="proposal-warnings-title">Deploy outcome unknown</p><p>' +
4174
4215
  esc(p.deployPending.queueReleased
4175
4216
  ? 'The approval queue was released. Check the live flow before approving again.'
4176
- : p.deployPending.reason) + '</p></div>'
4217
+ : p.deployPending.reason) + '</p><p>Proposal ' + esc(p.id) + ' / attempt ' + esc(p.deployPending.attemptId || '') + ' / tabs: ' + esc(p.flowId || p.payload.flowId) + '</p><button type="button" class="confirm-live-checked-btn" data-attempt-id="' + esc(p.deployPending.attemptId || '') + '" data-id="' + esc(p.id) + '">Confirm live checked</button></div>'
4177
4218
  : '';
4178
4219
  var warnBanner = deployBanner + renderReviewWarnings(p, cached);
4179
4220
  var stale = proposalIsStale(p);
@@ -4188,7 +4229,7 @@ ${as}
4188
4229
  '<header class="proposal-card-head">' +
4189
4230
  '<h3 class="proposal-heading" data-title-for="' + esc(p.id) + '">' + esc(title) + '</h3>' +
4190
4231
  '<span class="status-pill ' + esc(p.status) + '">' + esc(p.status === 'conflict' ? 'Conflict' : p.status) + '</span> ' +
4191
- renderRebaseChip(p, cached) + renderProposalMetaLine(p) +
4232
+ renderLandedAfterWithdraw(p) + renderRebaseChip(p, cached) + renderProposalMetaLine(p) +
4192
4233
  '</header>' +
4193
4234
  (createdBy ? createdBy + '<br/>' : '') +
4194
4235
  '<p class="proposal-plain-summary" data-plain-for="' + esc(p.id) + '">' +
@@ -4202,14 +4243,18 @@ ${as}
4202
4243
  '<div class="proposal-actions">' + actions + '</div>' +
4203
4244
  '</article>';
4204
4245
  }
4205
- `}a(Dg,"pageClientScriptPartB2Cards");function Hg(){return`
4246
+ `}a(Xg,"pageClientScriptPartB2Cards");function Qg(){return`
4247
+ function renderLandedAfterWithdraw(p) {
4248
+ return p.status === 'withdrawn' && p.landedAfterWithdraw
4249
+ ? '<span class="status-pill withdrawn">Landed after withdraw</span> ' : '';
4250
+ }
4206
4251
  function renderCompactProposal(p) {
4207
4252
  var title = titleForProposalCard(p);
4208
4253
  return '<div class="proposal-card-compact" data-id="' + esc(p.id) +
4209
4254
  '" role="button" tabindex="0" aria-expanded="false">' +
4210
4255
  '<strong>' + esc(title) + '</strong>' +
4211
4256
  '<span class="status-pill ' + esc(p.status) + '">' + esc(p.status) + '</span>' +
4212
- renderProposalMetaLine(p) +
4257
+ renderLandedAfterWithdraw(p) + renderProposalMetaLine(p) +
4213
4258
  '</div>' +
4214
4259
  '<div class="proposal-detail" id="proposal-' + esc(p.id) + '" data-id="' + esc(p.id) +
4215
4260
  '" data-status="' + esc(p.status) + '" hidden></div>';
@@ -4270,9 +4315,27 @@ ${as}
4270
4315
  '</div>';
4271
4316
  }
4272
4317
 
4318
+ function renderDeployUnknownBlocks(blocks) {
4319
+ return blocks.map(function (b) {
4320
+ return '<div role="alert" class="proposal-warnings">All approvals blocked. Deployment target tabs: ' +
4321
+ esc(b.flowIds.join(', ')) + ' \u2014 proposal ' + esc(b.proposalId) + ', attempt ' + esc(b.attemptId || '') +
4322
+ '. An earlier deploy may still land. <button type="button" class="confirm-live-checked-btn" data-attempt-id="' + esc(b.attemptId || '') + '" data-id="' + esc(b.proposalId) + '">Confirm live checked</button></div>';
4323
+ }).join('');
4324
+ }
4325
+
4273
4326
  function renderProposalsList() {
4274
4327
  var list = $('proposals-list');
4275
4328
  if (!list) return;
4329
+ var blockBanner = document.getElementById('deploy-unknown-blocks');
4330
+ if (!blockBanner && list.parentNode) {
4331
+ blockBanner = document.createElement('div');
4332
+ blockBanner.id = 'deploy-unknown-blocks';
4333
+ list.parentNode.insertBefore(blockBanner, list);
4334
+ }
4335
+ if (blockBanner) {
4336
+ blockBanner.innerHTML = renderDeployUnknownBlocks(deployUnknownBlocks);
4337
+ bindProposalActions(blockBanner);
4338
+ }
4276
4339
  var filters = $('proposals-filters');
4277
4340
  var pending = cachedProposals.filter(function (p) { return (p.status === 'pending' || p.status === 'conflict'); });
4278
4341
  var decided = cachedProposals.filter(function (p) { return (p.status !== 'pending' && p.status !== 'conflict'); });
@@ -4326,7 +4389,7 @@ ${as}
4326
4389
  bindProposalActions(list);
4327
4390
  focusProposalFromHash();
4328
4391
  }
4329
- `}a(Hg,"pageClientScriptPartB2CardsList");function jg(){return`
4392
+ `}a(Qg,"pageClientScriptPartB2CardsList");function ey(){return`
4330
4393
  async function refreshFlowLabels() {
4331
4394
  try {
4332
4395
  var data = await api('/flows');
@@ -4361,6 +4424,7 @@ ${as}
4361
4424
  decidedLoaded = decidedList.length;
4362
4425
  decidedTotal = Number(decidedData && decidedData.total) || decidedList.length;
4363
4426
  decidedHasMore = !!(decidedData && decidedData.hasMore);
4427
+ deployUnknownBlocks = (pendingData && pendingData.deployUnknownBlocks) || [];
4364
4428
  cachedProposals = ((pendingData && pendingData.proposals) || []).concat(decidedList);
4365
4429
  cachedProposals.forEach(function (p) {
4366
4430
  var id = proposalFlowId(p);
@@ -4501,7 +4565,7 @@ ${as}
4501
4565
  replaceLocationHash('#proposals');
4502
4566
  return true;
4503
4567
  }
4504
- `}a(jg,"pageClientScriptPartB2Focus");function Ug(){return`
4568
+ `}a(ey,"pageClientScriptPartB2Focus");function ty(){return`
4505
4569
  function bindDiffModeToggle(host, diff) {
4506
4570
  host.querySelectorAll('[data-diff-mode]').forEach(function (btn) {
4507
4571
  btn.addEventListener('click', function () {
@@ -4631,7 +4695,7 @@ ${as}
4631
4695
  esc(e.message || String(e)) + '</p>';
4632
4696
  }
4633
4697
  }
4634
- }`}a(Ug,"pageClientScriptPartB2Apply");function Bg(){return`
4698
+ }`}a(ty,"pageClientScriptPartB2Apply");function ny(){return`
4635
4699
 
4636
4700
  function dialogFocusables(dialog) {
4637
4701
  if (!dialog) return [];
@@ -4832,16 +4896,16 @@ ${as}
4832
4896
  renderProposalsList();
4833
4897
  }
4834
4898
 
4835
- async function doApproveProposal(id, deploymentType) {
4899
+ async function doApproveProposal(id, deploymentType, overrideDeployOutcomeUnknown) {
4836
4900
  proposalActionsInFlight += 1;
4837
4901
  try {
4838
- return await doApproveProposalInner(id, deploymentType);
4902
+ return await doApproveProposalInner(id, deploymentType, overrideDeployOutcomeUnknown);
4839
4903
  } finally {
4840
4904
  proposalActionsInFlight -= 1;
4841
4905
  }
4842
4906
  }
4843
4907
 
4844
- async function doApproveProposalInner(id, deploymentType) {
4908
+ async function doApproveProposalInner(id, deploymentType, overrideDeployOutcomeUnknown) {
4845
4909
  var flash = $('proposals-flash');
4846
4910
  var pBefore = cachedProposals.filter(function (x) { return x.id === id; })[0];
4847
4911
  var toastTitle = proposalToastTitle(pBefore);
@@ -4852,9 +4916,11 @@ ${as}
4852
4916
  }).length
4853
4917
  : 0;
4854
4918
  try {
4919
+ var approveBody = { deploymentType: deploymentType, mergedHash: proposalDiffCache[id] && proposalDiffCache[id].rebase && proposalDiffCache[id].rebase.mergedHash };
4920
+ if (overrideDeployOutcomeUnknown === true) approveBody.overrideDeployOutcomeUnknown = true;
4855
4921
  var result = await api('/proposals/' + encodeURIComponent(id) + '/approve', {
4856
4922
  method: 'POST',
4857
- body: JSON.stringify({ deploymentType: deploymentType, mergedHash: proposalDiffCache[id] && proposalDiffCache[id].rebase && proposalDiffCache[id].rebase.mergedHash })
4923
+ body: JSON.stringify(approveBody)
4858
4924
  });
4859
4925
  var viewOpts = { title: toastTitle, href: proposalViewHref(id), linkLabel: 'View' };
4860
4926
  if (result.status === 'rejected') {
@@ -4894,6 +4960,15 @@ ${as}
4894
4960
  loadLogs();
4895
4961
  } catch (e) {
4896
4962
  var code = e.body && e.body.code;
4963
+ if (code === 'deploy_outcome_unknown' && !overrideDeployOutcomeUnknown) {
4964
+ openConfirmDialog({
4965
+ title: 'Deployment outcome unknown',
4966
+ message: 'An earlier deployment can still overwrite this approval. Override the safety block and deploy anyway?',
4967
+ confirmLabel: 'Override & deploy', danger: true,
4968
+ onConfirm: function () { return doApproveProposal(id, deploymentType, true); }
4969
+ });
4970
+ return;
4971
+ }
4897
4972
  var errMsg = e.message || String(e);
4898
4973
  if (e.status === 409 && (code === 'rebase_ack_required' || code === 'proposal_conflict')) {
4899
4974
  errMsg = code === 'rebase_ack_required'
@@ -4951,13 +5026,14 @@ ${as}
4951
5026
  var pBefore = cachedProposals.filter(function (x) { return x.id === id; })[0];
4952
5027
  var toastTitle = proposalToastTitle(pBefore);
4953
5028
  try {
4954
- await api('/proposals/' + encodeURIComponent(id) + '/reject', {
5029
+ var rejected = await api('/proposals/' + encodeURIComponent(id) + '/reject', {
4955
5030
  method: 'POST',
4956
5031
  body: JSON.stringify({ reason: reason })
4957
5032
  });
4958
5033
  delete proposalDiffCache[id];
4959
5034
  if (!silent) {
4960
5035
  var okMsg = dismissed ? 'Proposal dismissed.' : 'Proposal rejected.';
5036
+ if (rejected.rejectedWhileDeployUnknown) okMsg += ' Warning: an earlier deploy may still land.';
4961
5037
  showFlash(flash, okMsg + (toastTitle ? ' \u2014 ' + toastTitle : ''), true);
4962
5038
  showToast(okMsg, true, {
4963
5039
  title: toastTitle,
@@ -4978,7 +5054,7 @@ ${as}
4978
5054
  throw e;
4979
5055
  }
4980
5056
  }
4981
- `}a(Bg,"pageClientScriptPartC");function qg(){return`
5057
+ `}a(ny,"pageClientScriptPartC");function ry(){return`
4982
5058
  var cachedLogs = [];
4983
5059
 
4984
5060
  function renderLogsTable(logs) {
@@ -5060,7 +5136,7 @@ ${as}
5060
5136
  showFlash(flash, e.message || String(e), false);
5061
5137
  }
5062
5138
  }
5063
- `}a(qg,"pageClientScriptPartCLogs");function zg(){return`
5139
+ `}a(ry,"pageClientScriptPartCLogs");function oy(){return`
5064
5140
  function applyStatus(s) {
5065
5141
  aiOn = !!s.aiFeaturesEnabled;
5066
5142
  canManage = !!(s.capabilities && s.capabilities.manageLinks);
@@ -5225,7 +5301,7 @@ ${as}
5225
5301
  });
5226
5302
  }
5227
5303
  }
5228
- `}a(zg,"pageClientScriptPartD");function Wg(){return`
5304
+ `}a(oy,"pageClientScriptPartD");function sy(){return`
5229
5305
  async function boot() {
5230
5306
  accessToken = readEditorToken();
5231
5307
  if (!accessToken) {
@@ -5245,7 +5321,7 @@ ${as}
5245
5321
  setupStickyNav();
5246
5322
  // Restore tab from hash (or default #status); keep #proposal=<id> until focus consumes it.
5247
5323
  var initialTab = initialTabFromHash(location.hash || '');
5248
- selectTab(initialTab, { skipHash: !!readProposalHashId() });
5324
+ selectTab(initialTab, { skipHash: !!readProposalHashId(), focusHeading: !!location.hash });
5249
5325
  if (initialTab === 'settings') loadSettings();
5250
5326
  if (initialTab === 'oauth') loadOAuthTab();
5251
5327
  if (aiOn) {
@@ -5317,23 +5393,28 @@ ${as}
5317
5393
 
5318
5394
  setupDialogs();
5319
5395
 
5320
- window.addEventListener('hashchange', function () {
5396
+ function routeFromLocation() {
5397
+ if (authGateShown) return;
5398
+ // A history traversal may emit both popstate and hashchange for the same URL.
5399
+ if (selectedLocationHash === String(location.hash || '')) return;
5321
5400
  var proposalId = readProposalHashId();
5322
5401
  if (proposalId) {
5323
- selectTab('proposals', { skipHash: true });
5402
+ selectTab('proposals', { skipHash: true, focusHeading: true });
5324
5403
  loadTabData('proposals');
5325
5404
  return;
5326
5405
  }
5327
5406
  var tab = tabFromHash(location.hash) || 'status';
5328
- selectTab(tab, { skipHash: true });
5407
+ selectTab(tab, { skipHash: true, focusHeading: true });
5329
5408
  loadTabData(tab);
5330
- });
5409
+ }
5410
+ window.addEventListener('hashchange', routeFromLocation);
5411
+ window.addEventListener('popstate', routeFromLocation);
5331
5412
 
5332
- ${Gg()}
5333
- ${Kg()}
5413
+ ${iy()}
5414
+ ${ay()}
5334
5415
  boot();
5335
5416
  })();
5336
- `}a(Wg,"pageClientScriptPartDBoot");function Gg(){return`
5417
+ `}a(sy,"pageClientScriptPartDBoot");function iy(){return`
5337
5418
  /*
5338
5419
  * Jump links to the editor (#flow/<id>, #node/<id>). A click first asks an
5339
5420
  * already-open editor tab (BroadcastChannel 'node-red-ai-access', answered by
@@ -5398,7 +5479,7 @@ ${Kg()}
5398
5479
  ev.stopPropagation();
5399
5480
  revealInEditor(a.getAttribute('data-reveal-kind'), a.getAttribute('data-reveal-id'), a.getAttribute('href'));
5400
5481
  });
5401
- `}a(Gg,"pageClientEditorJumpScript");function Kg(){return`
5482
+ `}a(iy,"pageClientEditorJumpScript");function ay(){return`
5402
5483
  /*
5403
5484
  * Auto-refresh when a tab left open becomes visible / focused again.
5404
5485
  * Throttled; skipped while a dialog (e.g. the one-time token modal) is open or
@@ -5428,7 +5509,7 @@ ${Kg()}
5428
5509
  var s = await api('/status');
5429
5510
  if (!isLatestLoad('status', gen)) return;
5430
5511
  applyStatus(s);
5431
- var sel = document.querySelector('[role="tab"][aria-selected="true"]');
5512
+ var sel = document.querySelector('nav.tabs button[aria-current="page"]');
5432
5513
  var tab = sel ? sel.getAttribute('data-tab') : '';
5433
5514
  if (aiOn) {
5434
5515
  if (canManage) loadLinks({ auto: true });
@@ -5448,7 +5529,7 @@ ${Kg()}
5448
5529
  });
5449
5530
  window.addEventListener('focus', autoRefresh);
5450
5531
 
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`
5532
+ `}a(ay,"pageClientAutoRefreshScript");function Ic(){return(Hg()+jg()+Ug()+Bg()+qg()+Wg()+zg()+Gg()+Kg()+Jg()+Vg()+Yg()+Zg()+Xg()+Qg()+ey()+ty()+ny()+ry()+oy()+Ec()+sy()).trim()}a(Ic,"pageClientScript");y();function dy(){return`
5452
5533
  :root {
5453
5534
  color-scheme: light;
5454
5535
  --bg: #eef2f5;
@@ -5481,7 +5562,7 @@ body {
5481
5562
  }
5482
5563
  main {
5483
5564
  width: 100%;
5484
- max-width: min(1640px, 97vw);
5565
+ max-width: 1200px;
5485
5566
  margin: 0 auto;
5486
5567
  padding: 2.25rem clamp(0.85rem, 2.5vw, 1.75rem) 3.5rem;
5487
5568
  }
@@ -5496,17 +5577,6 @@ h1 {
5496
5577
  line-height: 1.5;
5497
5578
  margin: 0 0 0.85rem;
5498
5579
  }
5499
- .how-it-works {
5500
- margin: 0 0 1.25rem;
5501
- padding: 0.75rem 0.9rem 0.75rem 1.6rem;
5502
- background: var(--accent-soft);
5503
- border-radius: 8px;
5504
- color: var(--ink);
5505
- line-height: 1.45;
5506
- font-size: 0.95rem;
5507
- }
5508
- .how-it-works li { margin: 0.2rem 0; }
5509
- .how-it-works strong { color: var(--accent); }
5510
5580
  /* Signed-out card: same look as the OAuth consent / sign-in page (light only). */
5511
5581
  .auth-gate {
5512
5582
  --sg-bg: #f3f4f6; --sg-card: #fff; --sg-text: #111827; --sg-muted: #4b5563; --sg-line: #e5e7eb;
@@ -5545,40 +5615,71 @@ body.signed-out { overflow: hidden; }
5545
5615
  .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); }
5546
5616
  .auth-gate a { color: var(--sg-focus); }
5547
5617
  @media (max-width: 420px) { .auth-gate .sg-actions { flex-direction: column-reverse; } .auth-gate .sg-secondary { flex: 1; } }
5548
- `}a(Jg,"pageStylesPartA");function Vg(){return`
5618
+ `}a(dy,"pageStylesPartA");function ly(){return`
5619
+ /* Layout uses the existing palette, including any host theme variable overrides. */
5620
+ .settings-layout { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 2rem; align-items: start; }
5549
5621
  nav.tabs {
5550
5622
  position: sticky;
5551
- top: 0;
5552
- z-index: 40;
5623
+ top: 1rem;
5553
5624
  display: flex;
5554
- flex-wrap: nowrap;
5555
- overflow-x: auto;
5556
- gap: 0.4rem;
5557
- margin: 0 0 1.1rem;
5558
- padding: 0.55rem 0;
5559
- background: var(--bg);
5560
- border-bottom: 1px solid transparent;
5561
- transition: box-shadow 0.15s ease, border-color 0.15s ease;
5562
- }
5563
- nav.tabs.is-stuck {
5564
- box-shadow: 0 4px 12px rgba(26, 35, 48, 0.08);
5565
- border-bottom-color: var(--line);
5566
- background: var(--soft);
5625
+ flex-direction: column;
5626
+ gap: 1rem;
5627
+ min-width: 0;
5628
+ padding: 0.25rem 0;
5629
+ }
5630
+ .nav-group-title {
5631
+ margin: 0 0 0.4rem;
5632
+ padding: 0 0.85rem;
5633
+ font-size: 0.75rem;
5634
+ font-weight: 600;
5635
+ letter-spacing: 0.06em;
5636
+ text-transform: uppercase;
5637
+ color: var(--muted);
5567
5638
  }
5568
5639
  nav.tabs button {
5569
- flex: 0 0 auto;
5570
- border: 1px solid var(--line);
5571
- background: var(--card);
5640
+ position: relative;
5641
+ display: flex;
5642
+ align-items: center;
5643
+ gap: 0.6rem;
5644
+ width: 100%;
5645
+ border: 0;
5646
+ background: transparent;
5572
5647
  color: var(--ink);
5573
- padding: 0.4rem 0.85rem;
5574
- border-radius: 8px;
5648
+ padding: 0.5rem 0.85rem;
5649
+ border-radius: 6px;
5575
5650
  cursor: pointer;
5576
5651
  font: inherit;
5652
+ text-align: left;
5577
5653
  }
5578
- nav.tabs button[aria-selected="true"] {
5654
+ nav.tabs button svg { flex: 0 0 16px; }
5655
+ nav.tabs button:hover { background: var(--soft); }
5656
+ nav.tabs button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
5657
+ nav.tabs button[aria-current="page"] { background: var(--accent-soft); font-weight: 600; }
5658
+ nav.tabs button[aria-current="page"]::before {
5659
+ content: "";
5660
+ position: absolute;
5661
+ left: 0;
5662
+ top: 0.45rem;
5663
+ bottom: 0.45rem;
5664
+ width: 3px;
5665
+ border-radius: 2px;
5579
5666
  background: var(--accent);
5580
- border-color: var(--accent);
5581
- color: #fff;
5667
+ }
5668
+ .settings-layout > section.panel { grid-column: 2; grid-row: 1; min-width: 0; }
5669
+ section.panel > h2 {
5670
+ padding-bottom: 0.65rem;
5671
+ border-bottom: 1px solid var(--line);
5672
+ font-size: 1.65rem;
5673
+ font-weight: 500;
5674
+ }
5675
+ section.panel > h3 { margin: 1.4rem 0 0.75rem; font-size: 1.15rem; font-weight: 600; }
5676
+ .settings-subpart > h3 { margin: 1.4rem 0 0.75rem; font-size: 1.15rem; }
5677
+ @media (max-width: 768px) {
5678
+ .settings-layout { grid-template-columns: minmax(0, 1fr); gap: 1rem; }
5679
+ .settings-layout > section.panel { grid-column: 1; grid-row: 2; padding: 1rem; }
5680
+ nav.tabs { position: static; flex-direction: column; gap: 0.75rem; }
5681
+ .nav-group { width: 100%; min-width: 0; }
5682
+ nav.tabs button { font-size: 0.95rem; }
5582
5683
  }
5583
5684
  .tab-badge {
5584
5685
  display: inline-block;
@@ -5597,7 +5698,7 @@ nav.tabs button[aria-selected="true"] {
5597
5698
  overflow: hidden;
5598
5699
  text-overflow: ellipsis;
5599
5700
  }
5600
- nav.tabs button[aria-selected="true"] .tab-badge {
5701
+ nav.tabs button[aria-current="page"] .tab-badge {
5601
5702
  background: rgba(255, 255, 255, 0.92);
5602
5703
  color: var(--accent);
5603
5704
  }
@@ -5609,9 +5710,9 @@ section.panel {
5609
5710
  padding: 1.15rem 1.25rem 1.35rem;
5610
5711
  }
5611
5712
  section.panel.active { display: block; }
5612
- section.panel h2 {
5713
+ section.panel > h2 {
5613
5714
  margin: 0 0 0.75rem;
5614
- font-size: 1.15rem;
5715
+ font-size: 1.65rem;
5615
5716
  }
5616
5717
  .warning {
5617
5718
  background: var(--warn-bg);
@@ -5624,11 +5725,12 @@ section.panel h2 {
5624
5725
  .warning p { margin: 0; line-height: 1.45; }
5625
5726
  dl.status {
5626
5727
  display: grid;
5627
- grid-template-columns: 11rem 1fr;
5628
- gap: 0.45rem 1rem;
5728
+ grid-template-columns: minmax(0, 1fr);
5729
+ gap: 0.25rem;
5629
5730
  margin: 0;
5630
5731
  }
5631
5732
  dt { color: var(--muted); }
5733
+ dl.status dd { margin-bottom: 0.75rem; }
5632
5734
  dd {
5633
5735
  margin: 0;
5634
5736
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
@@ -5641,7 +5743,7 @@ dd {
5641
5743
  padding: 0.22rem 0.55rem;
5642
5744
  border-radius: 6px;
5643
5745
  cursor: help;
5644
- background: var`}a(Vg,"pageStylesPartA2");function Yg(){return`(--accent-soft);
5746
+ background: var`}a(ly,"pageStylesPartA2");function cy(){return`(--accent-soft);
5645
5747
  color: var(--accent);
5646
5748
  font-size: 0.82rem;
5647
5749
  font-weight: 650;
@@ -5832,7 +5934,7 @@ dialog.token-modal { width: min(720px, 94vw); max-width: min(720px, 94vw); max-h
5832
5934
  color: var(--muted);
5833
5935
  font-size: 0.92rem;
5834
5936
  }
5835
- `}a(Yg,"pageStylesPartB");function Xg(){return`
5937
+ `}a(cy,"pageStylesPartB");function uy(){return`
5836
5938
  .logs-toolbar { margin-bottom: 0.65rem; }
5837
5939
  table.audit-table {
5838
5940
  table-layout: fixed;
@@ -5908,7 +6010,7 @@ table.audit-table {
5908
6010
  cursor: help;
5909
6011
  border-bottom: 1px dotted var(--line);
5910
6012
  }
5911
- `}a(Xg,"pageStylesPartC");function Zg(){return`
6013
+ `}a(uy,"pageStylesPartC");function py(){return`
5912
6014
  .proposal-diff {
5913
6015
  margin: 0.65rem 0;
5914
6016
  padding: 0.55rem 0.65rem;
@@ -5976,7 +6078,7 @@ table.audit-table {
5976
6078
  .prop-change { margin: 0.25rem 0 0.35rem; font-size: 0.88rem; }
5977
6079
  .prop-change-scalar.split {
5978
6080
  display: grid;
5979
- grid-template-columns: 1fr auto 1fr;
6081
+ grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
5980
6082
  gap: 0.45rem;
5981
6083
  align-items: start;
5982
6084
  }
@@ -5989,7 +6091,7 @@ table.audit-table {
5989
6091
  color: var(--muted);
5990
6092
  margin-bottom: 0.15rem;
5991
6093
  }
5992
- `}a(Zg,"pageStylesPartD");function Qg(){return`
6094
+ `}a(py,"pageStylesPartD");function fy(){return`
5993
6095
  .diff-block {
5994
6096
  margin-top: 0.25rem;
5995
6097
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
@@ -6036,7 +6138,7 @@ table.audit-table {
6036
6138
  .diff-blank { background: #f0f3f6; color: transparent; }
6037
6139
  .diff-split {
6038
6140
  display: grid;
6039
- grid-template-columns: 1fr 1fr;
6141
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
6040
6142
  gap: 0;
6041
6143
  border: 1px solid var(--line);
6042
6144
  border-radius: 5px;
@@ -6071,7 +6173,7 @@ table.audit-table {
6071
6173
  }
6072
6174
  .diff-before { color: var(--danger); word-break: break-word; }
6073
6175
  .diff-after { color: var(--ok); word-break: break-word; }
6074
- `}a(Qg,"pageStylesPartE");function ey(){return`
6176
+ `}a(fy,"pageStylesPartE");function hy(){return`
6075
6177
  .proposal-detail {
6076
6178
  scroll-margin-top: var(--nav-height, 6rem);
6077
6179
  }
@@ -6197,18 +6299,16 @@ table.audit-table {
6197
6299
  .audit-table .col-action { width: 5.5rem; min-width: 5.5rem; }
6198
6300
  .audit-table .col-actor { width: 7rem; min-width: 6rem; }
6199
6301
  }
6200
- `}a(ey,"pageStylesPartF");function ty(){return`
6302
+ `}a(hy,"pageStylesPartF");function my(){return`
6201
6303
  .dash-grid {
6202
6304
  display: grid;
6203
- grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
6305
+ grid-template-columns: minmax(0, 1fr);
6204
6306
  gap: 0.65rem;
6205
6307
  margin: 0 0 1rem;
6206
6308
  }
6207
6309
  .dash-card {
6208
- background: var(--soft);
6209
- border: 1px solid var(--line);
6210
- border-radius: 9px;
6211
- padding: 0.75rem 0.85rem;
6310
+ border-bottom: 1px solid var(--line);
6311
+ padding: 0.75rem 0;
6212
6312
  }
6213
6313
  .dash-card-clickable { cursor: pointer; }
6214
6314
  .dash-card-clickable:hover { border-color: var(--accent); background: var(--accent-soft); }
@@ -6430,7 +6530,7 @@ button.id-copy:focus {
6430
6530
  background: var(--code-bg, var(--soft));
6431
6531
  border-radius: 4px;
6432
6532
  }
6433
- `}a(ty,"pageStylesPartG");function ny(){return`
6533
+ `}a(my,"pageStylesPartG");function gy(){return`
6434
6534
  .stale-group {
6435
6535
  margin: 1rem 0 0;
6436
6536
  border: 1px solid var(--warn-border);
@@ -6594,27 +6694,31 @@ dialog h3 { margin: 0 0 0.4rem; }
6594
6694
  margin-left: auto;
6595
6695
  }
6596
6696
  .debug-toggle input { width: auto; }
6597
- `}a(ny,"pageStylesPartH");function ry(){return`
6697
+ `}a(gy,"pageStylesPartH");function yy(){return`
6598
6698
  .card {
6599
6699
  background: var(--card);
6600
- border: 1px solid var(--line);
6601
- border-radius: 10px;
6602
- padding: 1rem 1.1rem;
6603
- margin: 0 0 1rem;
6700
+ border: 0;
6701
+ border-bottom: 1px solid var(--line);
6702
+ padding: 1.25rem 0;
6703
+ margin: 0;
6604
6704
  }
6605
6705
  .card h3 { margin: 0 0 0.75rem; font-size: 1.05rem; }
6606
6706
  .card-head { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; margin-bottom: 0.5rem; }
6607
6707
  .card-head h3 { margin: 0; }
6608
6708
  .settings-grid {
6609
6709
  display: grid;
6610
- grid-template-columns: repeat(auto-fit, minmax(min(100%, 540px), 1fr));
6611
- gap: 1rem;
6710
+ grid-template-columns: minmax(0, 1fr);
6711
+ gap: 0;
6612
6712
  align-items: start;
6613
- margin-bottom: 1rem;
6713
+ margin-bottom: 0;
6614
6714
  }
6615
6715
  .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); }
6716
+ /* Setting groups and form fields follow the same vertical reading order. */
6717
+ .settings-subpart { padding-bottom: 1.25rem; border-bottom: 1px solid var(--line); }
6718
+ section.panel:not(#section-proposals) .row { flex-direction: column; align-items: flex-start; }
6719
+ .logs-toolbar { padding-bottom: 1rem; border-bottom: 1px solid var(--line); }
6720
+ .logs-toolbar .debug-toggle { margin-left: 0; }
6721
+ section.panel { scroll-margin-top: 1rem; }
6618
6722
  .proposal-conflict, .conflict-choice { padding: 1rem; margin: 0.75rem 0; border: 1px solid var(--warn-border); border-radius: 8px; background: var(--warn-bg); }
6619
6723
  .conflict-option { margin: 0.8rem 0; }
6620
6724
  .conflict-values dl { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.75rem; }
@@ -6674,27 +6778,19 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
6674
6778
  .conflict-list { margin: 0.4rem 0 0 1.2rem; padding: 0; }
6675
6779
  .conflict-list li { margin: 0.25rem 0; }
6676
6780
  @media (max-width: 640px) {
6677
- nav.tabs { overflow-x: auto; flex-wrap: nowrap; }
6678
- nav.tabs button {
6679
- flex: 0 0 auto; flex: 0 0 auto; }
6680
6781
  .field-line { flex-wrap: wrap; }
6681
6782
  }
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`
6783
+ `}a(yy,"pageStylesDashboard");function Lc(){return(dy()+ly()+cy()+uy()+py()+fy()+hy()+my()+gy()+yy()).trim()}a(Lc,"pageStyles");function wy(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;")}a(wy,"escapeHtml");function by(){return`
6683
6784
  <header class="page-header">
6684
6785
  <h1>Node-RED AI Access</h1>
6685
6786
  <p class="lede">
6686
6787
  Let an AI assistant suggest changes to your flows \u2014 nothing changes until you approve.
6687
6788
  </p>
6688
- <ol class="how-it-works" id="how-it-works" aria-label="How it works">
6689
- <li><strong>Create a link</strong> \u2014 temporary access for your AI assistant.</li>
6690
- <li><strong>Give it to your AI</strong> \u2014 paste the link in the chat.</li>
6691
- <li><strong>Review proposals</strong> \u2014 approve to deploy, or reject.</li>
6692
- </ol>
6693
6789
  </header>
6694
6790
  <div id="boot-flash" class="flash" hidden></div>
6695
6791
  <div id="auth-gate" class="auth-gate" hidden>
6696
6792
  <div class="sg-card" role="dialog" aria-modal="true" aria-labelledby="auth-gate-title">
6697
- <div class="sg-head">${Ui}<div class="sg-brand"><b>Node-RED AI Access</b><span>Dashboard</span></div></div>
6793
+ <div class="sg-head">${Xi}<div class="sg-brand"><b>Node-RED AI Access</b><span>Dashboard</span></div></div>
6698
6794
  <div class="sg-body">
6699
6795
  <h1 id="auth-gate-title">Sign in to Node-RED</h1>
6700
6796
  <p class="sg-sub">AI Access uses your Node-RED editor account. Signing in here also signs in the editor in this browser.</p>
@@ -6714,20 +6810,21 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
6714
6810
  </div>
6715
6811
  </div>
6716
6812
  <div id="toast-host" class="toast-host" aria-live="polite"></div>
6717
- <nav class="tabs" aria-label="Admin views" hidden id="main-nav">
6718
- <button type="button" data-tab="status" aria-selected="true">Overview</button>
6719
- <button type="button" data-tab="settings" aria-selected="false">Settings</button>
6720
- <button type="button" data-tab="links" aria-selected="false">Access tokens</button>
6721
- <button type="button" data-tab="oauth" aria-selected="false">OAuth</button>
6722
- <button type="button" data-tab="proposals" aria-selected="false">
6723
- Proposals <span class="tab-badge" id="proposals-pending-badge" hidden>0</span>
6724
- </button>
6725
- <button type="button" data-tab="logs" aria-selected="false">Audit log</button>
6726
- </nav>`}a(sy,"renderMcpPageHeader");function iy(){return`
6813
+ <div class="settings-layout">
6814
+ ${vy()}`}a(by,"renderMcpPageHeader");function vy(){return`<nav class="tabs" aria-label="Admin views" hidden id="main-nav">
6815
+ ${[{title:"Overview",items:[["status","Overview","M8 1 1 7v8h5v-5h4v5h5V7L8 1Zm0 2 5.5 4.8V13H12V8H4v5H2.5V7.8Z"]]},{title:"Access",items:[["links","Access tokens","M10.5 1a4.5 4.5 0 0 0-4.3 5.8L1 12v3h4v-2h2v-2h2l.8-.8A4.5 4.5 0 1 0 10.5 1Zm0 1.5a3 3 0 1 1 0 6 3 3 0 0 1 0-6ZM2.5 12.6l4.5-4.5 1 1-1.4 1.4H5.5v1H4v2H2.5Z"],["oauth","OAuth","M8 0 1 3v5c0 4 7 8 7 8s7-4 7-8V3L8 0Zm0 1.6 5.5 2.4v4c0 2.5-3.8 5.3-5.5 6.3C6.3 13.3 2.5 10.5 2.5 8V4Z"]]},{title:"Proposals",items:[["proposals","Proposals","M3 1a2 2 0 1 0 0 4 2 2 0 0 0 0-4ZM2.25 6v5a2 2 0 1 0 1.5 0V6ZM11 1v3H7v1.5h4V9h1.5V5.5H16V4h-3.5V1Z"]]},{title:"Advanced",items:[["settings","Settings","M0 3h3V1h1.5v2H16v1.5H4.5V7H3V4.5H0Zm0 8h10V9h1.5v2H16v1.5h-4.5V15H10v-2.5H0Z"],["logs","Audit log","M3 1h10v14H3V1Zm1.5 1.5v11h7v-11ZM6 4h4v1.5H6Zm0 3h4v1.5H6Zm0 3h4v1.5H6Z"]]}].map(t=>`<div class="nav-group"${t.items.length===1&&t.items[0]?.[1]===t.title?"":` role="group" aria-labelledby="nav-group-${t.title.toLowerCase()}"`}>
6816
+ <div class="nav-group-title" id="nav-group-${t.title.toLowerCase()}"${t.items.length===1&&t.items[0]?.[1]===t.title?' aria-hidden="true"':""}>${t.title}</div>
6817
+ ${t.items.map(([n,r,o])=>`<button type="button" data-tab="${n}" aria-controls="section-${n}" aria-current="${n==="status"?"page":"false"}">
6818
+ <svg aria-hidden="true" focusable="false" width="16" height="16" viewBox="0 0 16 16" fill="currentColor"><path d="${o}"/></svg>
6819
+ <span>${r}</span>${n==="proposals"?' <span class="tab-badge" id="proposals-pending-badge" hidden>0</span>':""}
6820
+ </button>`).join("")}
6821
+ </div>`).join("")}
6822
+ </nav>`}a(vy,"renderMcpPageNav");function ky(){return`
6727
6823
  <section class="panel active" id="section-status" aria-label="Overview" hidden>
6728
- <h2>Overview</h2>
6824
+ <h2 tabindex="-1">Overview</h2>
6729
6825
  <div id="status-flash" class="flash" hidden></div>
6730
6826
  <aside id="status-warning" class="warning" role="status" hidden></aside>
6827
+ <h3>Activity</h3>
6731
6828
  <div class="dash-grid" id="status-dashboard">
6732
6829
  <div class="dash-card">
6733
6830
  <div class="dash-label">AI access</div>
@@ -6767,20 +6864,20 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
6767
6864
  <div class="row" style="margin-top:1rem">
6768
6865
  <button type="button" class="action secondary" id="refresh-status">Refresh</button>
6769
6866
  </div>
6770
- </section>`}a(iy,"renderMcpPageStatusSection");function ay(){return`
6867
+ </section>`}a(ky,"renderMcpPageStatusSection");function Ay(){return`
6771
6868
  <section class="panel" id="section-links" aria-label="Access tokens" hidden>
6772
- <h2>Access tokens</h2>
6869
+ <h2 tabindex="-1">Access tokens</h2>
6773
6870
  <p class="muted" id="links-intro">
6774
6871
  Create a token for the channel your AI uses: a GET link to paste in chat,
6775
6872
  a REST Bearer, or an MCP token. It can read flows and submit proposals \u2014
6776
6873
  it cannot deploy anything until you approve.
6777
6874
  </p>
6778
6875
  <div id="links-flash" class="flash" hidden></div>
6779
- <div id="links-manage"></div>
6780
- <div id="links-list"></div>
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`
6876
+ <div class="settings-subpart"><h3>Create access token</h3><div id="links-manage"></div></div>
6877
+ <h3>Access tokens</h3><div id="links-list"></div>
6878
+ </section>`}a(Ay,"renderMcpPageLinksSection");function xe(e){return`<span class="lock-badge" id="lock-${e}" hidden>Locked by settings.js</span>`}a(xe,"lockBadge");function xy(){return`
6782
6879
  <section class="panel" id="section-settings" aria-label="Settings" hidden>
6783
- <h2>Settings</h2>
6880
+ <h2 tabindex="-1">Settings</h2>
6784
6881
  <p class="muted">Changes save automatically and apply immediately (no restart).</p>
6785
6882
  <div id="settings-flash" class="flash" hidden></div>
6786
6883
  <p id="settings-readonly" class="notice" hidden>Read-only: full admin permission (*) is required to change these settings.</p>
@@ -6791,13 +6888,13 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
6791
6888
  <label class="switch-label"><input type="checkbox" id="set-enabled" />
6792
6889
  <span><strong>Enable AI Access features</strong>
6793
6890
  <span class="hint">When off, access tokens, proposals, and AI tools are disabled.</span></span></label>
6794
- ${be("enabled")}
6891
+ ${xe("enabled")}
6795
6892
  </div>
6796
6893
  <div class="form-row toggle-row" id="row-allow-without" hidden>
6797
6894
  <label class="switch-label"><input type="checkbox" id="set-allow-without" />
6798
6895
  <span><strong>Allow AI Access without adminAuth (local-only)</strong>
6799
6896
  <span class="hint danger-text">Not for production. Anyone who can reach the editor admin port can use AI features.</span></span></label>
6800
- ${be("allow-without")}
6897
+ ${xe("allow-without")}
6801
6898
  </div>
6802
6899
  <div class="form-row">
6803
6900
  <label for="set-deploy">Default deploy type</label>
@@ -6807,7 +6904,7 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
6807
6904
  <option value="flows">Changed flows</option>
6808
6905
  <option value="full">Full redeploy</option>
6809
6906
  </select>
6810
- ${be("deploy")}
6907
+ ${xe("deploy")}
6811
6908
  </div>
6812
6909
  <p class="hint danger-text" id="set-deploy-full-warn" hidden>Full redeploy restarts the entire runtime.</p>
6813
6910
  </div>
@@ -6816,14 +6913,14 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
6816
6913
  <div class="field-line">
6817
6914
  <input type="number" id="set-ttl" min="1" step="1" class="num" />
6818
6915
  <span class="unit">hours (minimum 1)</span>
6819
- ${be("ttl")}
6916
+ ${xe("ttl")}
6820
6917
  </div>
6821
6918
  </div>
6822
6919
  <div class="form-row">
6823
6920
  <label for="set-proposal-ttl">Proposal expiry (days)</label>
6824
6921
  <div class="field-line">
6825
6922
  <input type="number" id="set-proposal-ttl" min="1" max="365" step="1" value="7" class="num" />
6826
- ${be("proposal-ttl")}
6923
+ ${xe("proposal-ttl")}
6827
6924
  </div>
6828
6925
  <p class="hint">Open proposals expire after this many days from submission (1\u2013365, default 7).</p>
6829
6926
  </div>
@@ -6832,7 +6929,7 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
6832
6929
  <div class="field-line">
6833
6930
  <input type="number" id="set-max-links" min="1" step="1" class="num" />
6834
6931
  <span class="unit">tokens</span>
6835
- ${be("max-links")}
6932
+ ${xe("max-links")}
6836
6933
  </div>
6837
6934
  </div>
6838
6935
  </div>
@@ -6845,7 +6942,7 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
6845
6942
  <div class="field-line">
6846
6943
  <input type="url" id="set-public-url" placeholder="https://nodered.example.com" class="grow" />
6847
6944
  <button type="button" class="action secondary" id="set-check-public-url">Check</button>
6848
- ${be("public-url")}
6945
+ ${xe("public-url")}
6849
6946
  </div>
6850
6947
  <div id="set-public-url-warning" class="hint warn-text" role="status" hidden></div>
6851
6948
  <div id="set-check-result" class="check-result" aria-live="polite" hidden></div>
@@ -6863,7 +6960,7 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
6863
6960
  <label for="set-cors">Allowed origins <span class="hint">(comma separated)</span></label>
6864
6961
  <div class="field-line">
6865
6962
  <input type="text" id="set-cors" class="grow" placeholder="https://app.example.com" />
6866
- ${be("cors")}
6963
+ ${xe("cors")}
6867
6964
  </div>
6868
6965
  </div>
6869
6966
  </div>
@@ -6878,7 +6975,7 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
6878
6975
  <input type="text" id="set-secret-new" class="grow" maxlength="40" placeholder="e.g. PIN or SIGNING_KEY" aria-label="New secret name pattern" data-no-autosave="1" />
6879
6976
  <button type="button" class="action secondary" id="set-secret-add">Add</button>
6880
6977
  <button type="button" class="action secondary" id="set-secret-reset">Reset to defaults</button>
6881
- ${be("secret-patterns")}
6978
+ ${xe("secret-patterns")}
6882
6979
  </div>
6883
6980
  <p class="hint">Letters, digits, <code>_</code> or <code>-</code> (2\u201340 characters). An empty list turns off name-based redaction; token formats are still redacted.</p>
6884
6981
  </div>
@@ -6891,7 +6988,7 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
6891
6988
  <div class="field-line">
6892
6989
  <input type="number" id="set-rate-window" min="1" step="1" class="num" />
6893
6990
  <span class="unit">ms</span>
6894
- ${be("rate-window")}
6991
+ ${xe("rate-window")}
6895
6992
  </div>
6896
6993
  </div>
6897
6994
  <div class="form-row">
@@ -6899,7 +6996,7 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
6899
6996
  <div class="field-line">
6900
6997
  <input type="number" id="set-rate-ip" min="1" step="1" class="num" />
6901
6998
  <span class="unit">requests / window</span>
6902
- ${be("rate-ip")}
6999
+ ${xe("rate-ip")}
6903
7000
  </div>
6904
7001
  </div>
6905
7002
  <div class="form-row">
@@ -6907,15 +7004,15 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
6907
7004
  <div class="field-line">
6908
7005
  <input type="number" id="set-rate-token" min="1" step="1" class="num" />
6909
7006
  <span class="unit">requests / window</span>
6910
- ${be("rate-token")}
7007
+ ${xe("rate-token")}
6911
7008
  </div>
6912
7009
  </div>
6913
7010
  <p class="hint" id="set-trust-proxy-inline"></p>
6914
7011
  </div>
6915
7012
  </div>
6916
- </section>`}a(dy,"renderMcpPageSettingsSection");function ly(){return`
7013
+ </section>`}a(xy,"renderMcpPageSettingsSection");function Sy(){return`
6917
7014
  <section class="panel" id="section-oauth" aria-label="OAuth" hidden>
6918
- <h2>MCP OAuth</h2>
7015
+ <h2 tabindex="-1">MCP OAuth</h2>
6919
7016
  <p class="muted">MCP clients (ChatGPT, Claude, \u2026) can request read or propose access through OAuth.</p>
6920
7017
  <div id="oauth-flash" class="flash" hidden></div>
6921
7018
  <div id="oauth-route-warning" class="warning" role="status" hidden></div>
@@ -6926,7 +7023,7 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
6926
7023
  <label class="switch-label"><input type="checkbox" id="oauth-enabled" />
6927
7024
  <span><strong>Enable MCP OAuth</strong>
6928
7025
  <span class="hint">Also available in the editor sidebar (AI Access tab).</span></span></label>
6929
- ${be("oauth")}
7026
+ ${xe("oauth")}
6930
7027
  </div>
6931
7028
  <div class="form-row">
6932
7029
  <span class="label">Server URL for MCP clients</span>
@@ -6988,9 +7085,9 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
6988
7085
  <button type="button" class="action secondary" id="oauth-refresh">Refresh</button></div>
6989
7086
  <div id="oauth-clients" aria-live="polite"><p class="muted">Loading\u2026</p></div>
6990
7087
  </div>
6991
- </section>`}a(ly,"renderMcpPageOAuthSection");function cy(){return`
7088
+ </section>`}a(Sy,"renderMcpPageOAuthSection");function Ry(){return`
6992
7089
  <section class="panel" id="section-proposals" aria-label="Proposals" hidden>
6993
- <h2>Proposals</h2>
7090
+ <h2 tabindex="-1">Proposals</h2>
6994
7091
  <p id="proposals-hint" class="muted"></p>
6995
7092
  <div class="filter-row" id="proposals-filters" hidden>
6996
7093
  <button type="button" class="filter-chip" data-filter="pending" aria-pressed="true">
@@ -7006,9 +7103,9 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
7006
7103
  </div>
7007
7104
  <div id="proposals-flash" class="flash" hidden></div>
7008
7105
  <div id="proposals-list"><p class="muted">Loading\u2026</p></div>
7009
- </section>`}a(cy,"renderMcpPageProposalsSection");function uy(){return`
7106
+ </section>`}a(Ry,"renderMcpPageProposalsSection");function Ty(){return`
7010
7107
  <section class="panel" id="section-logs" aria-label="Audit log" hidden>
7011
- <h2>Audit log</h2>
7108
+ <h2 tabindex="-1">Audit log</h2>
7012
7109
  <div id="logs-flash" class="flash" hidden></div>
7013
7110
  <div class="row logs-toolbar">
7014
7111
  <label>Show
@@ -7022,7 +7119,7 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
7022
7119
  </label>
7023
7120
  </div>
7024
7121
  <div id="logs-list"><p class="muted">Loading\u2026</p></div>
7025
- </section>`}a(uy,"renderMcpPageLogsSection");function py(){return`
7122
+ </section>`}a(Ty,"renderMcpPageLogsSection");function Py(){return`
7026
7123
  <dialog id="reject-dialog">
7027
7124
  <form method="dialog" id="reject-form">
7028
7125
  <h3>Reject proposal</h3>
@@ -7064,7 +7161,7 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
7064
7161
  <button type="submit" value="confirm" class="action danger" id="confirm-action-btn">Confirm</button>
7065
7162
  </div>
7066
7163
  </form>
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>
7164
+ </dialog>`}a(Py,"renderMcpPageDialogs");function Nc(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>
7068
7165
  <html lang="en">
7069
7166
  <head>
7070
7167
  <meta charset="utf-8" />
@@ -7072,23 +7169,24 @@ section.panel { scroll-margin-top: var(--nav-height, 6rem); }
7072
7169
  <meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" />
7073
7170
  <meta http-equiv="Pragma" content="no-cache" />
7074
7171
  <title>Node-RED AI Access</title>
7075
- <style>${bc()}</style>
7172
+ <style>${Lc()}</style>
7076
7173
  </head>
7077
7174
  <body>
7078
7175
  <main
7079
7176
  id="mcp-root"
7080
- data-config="${oy(JSON.stringify(t))}"
7081
- >${sy()}
7082
- ${iy()}
7083
- ${dy()}
7084
- ${ay()}
7085
- ${ly()}
7086
- ${cy()}
7087
- ${uy()}
7088
- ${py()}
7177
+ data-config="${wy(JSON.stringify(t))}"
7178
+ >${by()}
7179
+ ${ky()}
7180
+ ${xy()}
7181
+ ${Ay()}
7182
+ ${Sy()}
7183
+ ${Ry()}
7184
+ ${Ty()}
7185
+ </div>
7186
+ ${Py()}
7089
7187
  </main>
7090
- <script>${wc()}</script>
7188
+ <script>${Ic()}</script>
7091
7189
  </body>
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});
7190
+ </html>`}a(Nc,"renderMcpPage");qe();lt();y();Ee();tt();de();Gt();le();ct();en();qe();Bo();lt();Ne();function $e(e,t,n,r={}){e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("Content-Type","application/json; charset=utf-8");let o={error:ut[n]??"Request failed",code:n,...r},s=Ae(n,"rest");s&&typeof o.hint!="string"&&(o.hint=s),e.status(t).json(o)}a($e,"sendSafeJsonError");function tr(e){let t={kind:"admin"};return typeof e.user?.username=="string"&&(t.username=e.user.username),t}a(tr,"adminActor");function Fc(e,t,n,r,o){return async(s,i)=>{if(!s.accessLink){i.setHeader("WWW-Authenticate",ge),i.status(401).json({error:He,code:ht});return}try{let d=Z(s,r),l=ve(s.query),c=await Ot(e,t,s.accessLink,s.body??{},n,d,o),u=se(e),p=it(e,e.get(c.id),s.accessLink.id),f=c.warnings;i.status(201).json({...ue(c,{...d,include:l}),mine:!0,...at(p),...me(u),...f&&f.length>0?{warnings:f}:{}})}catch(d){let l=J(d,"rest");if(l){i.status(l.statusCode).json(l.body);return}z(i,d);return}}}a(Fc,"createProposalHandler");function Oc(e,t,n){return async(r,o)=>{let s=await dt(t),i=s?.rev,d=s?ko(s.flows):void 0,l={...Z(r,n),...d?{liveFlowLabels:d}:{}},c=ve(r.query),u;try{u=Oo(r.query)}catch(k){z(o,k);return}let p=s?ze(e,s).expired:se(e),f=r.accessLink;if(f){let k=Dn(e,f,l,u),$=k.proposals.map(C=>{let O=be(e,f,C,{...l,include:c},{includeConflicts:!1});return!U(O.status)||i===void 0?O:{...O,baseRevChanged:O.expectedRev!==i}});o.json({proposals:$,...rn({...k,returned:k.proposals.length}),...me(p)});return}let h={};u.flowId&&(h.flowId=u.flowId);let m=rd(e.list(h).filter(k=>Si(k.status,u.status)),{...l,include:c}),g=u.offset??0,v=(u.limit!==void 0?m.slice(g,g+u.limit):g>0?m.slice(g):m).map(k=>!U(k.status)||i===void 0?k:{...k,baseRevChanged:k.expectedRev!==i});o.json({proposals:v,deployUnknownBlocks:e.list().filter(k=>k.deployPending?.queueReleased).map(k=>({proposalId:k.id,attemptId:k.deployPending?.attemptId,flowIds:[k.payload.flowId]})),...rn({total:m.length,limit:u.limit??null,offset:g,returned:v.length}),...me(p)})}}a(Oc,"listProposalsHandler");function $c(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=Z(r,t),d=ve(r.query),l=await dt(n);try{if(r.accessLink){let c=bt(e,r.accessLink,s,i);o.json(be(e,r.accessLink,c,{...i,include:d},{...l?{live:l}:{}}));return}l?ze(e,l):se(e),o.json(ue(e.get(s),{...i,include:d}))}catch(c){if(c instanceof ye){$e(o,404,c.code);return}let u=J(c,"rest");if(u){o.status(u.statusCode).json(u.body);return}z(o,c);return}}}a($c,"getProposalHandler");function Mc(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.overrideDeployOutcomeUnknown!==void 0){if(typeof s.overrideDeployOutcomeUnknown!="boolean"){r.status(400).json({error:"overrideDeployOutcomeUnknown must be boolean",code:"invalid_body"});return}i.overrideDeployOutcomeUnknown=s.overrideDeployOutcomeUnknown}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,tr(n),i);r.json(ce(d,Z(n,t)))}catch(d){if(d instanceof et){$e(r,409,d.code,{conflicts:d.conflicts});return}if(d instanceof sn){$e(r,409,d.code,{mergedHash:d.mergedHash,rebaseState:d.rebaseState});return}if(d instanceof At||d instanceof an||d instanceof $t||d instanceof dn){$e(r,d.statusCode,d.code);return}if(d instanceof At||d instanceof ye||d instanceof Q||d instanceof Le||d instanceof he){$e(r,d.statusCode,d.code);return}z(r,d);return}}}a(Mc,"approveProposalHandler");function Dc(e,t){return(n,r)=>{let o=n.body??{};try{let s=e.confirmLiveChecked(String(n.params.id),tr(n),o.attemptId);r.json(ce(s,Z(n,t)))}catch(s){if(s instanceof At||s instanceof ye){$e(r,s.statusCode,s.code);return}z(r,s)}}}a(Dc,"confirmProposalLiveCheckedHandler");function Hc(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=ce(e.reject(o,tr(n),i),Z(n,t));r.json(d)}catch(d){if(d instanceof At||d instanceof ye||d instanceof Q||d instanceof he){$e(r,d.statusCode,d.code);return}z(r,d);return}}}a(Hc,"rejectProposalHandler");function jc(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=Z(r,n),l=ve(r.query);try{if(!r.accessLink){let u=Pi(e,t,tr(r),s,i.reason,d);o.json(ue(u,{...d,include:l}));return}let c=Hn(e,t,r.accessLink,s,i.reason,d);o.json(be(e,r.accessLink,c,{...d,include:l}))}catch(c){let u=J(c,"rest");if(u){$e(o,u.statusCode,u.body.code);return}$e(o,500,"internal_error");return}}}a(jc,"withdrawProposalHandler");function Uc(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?bt(e,n.accessLink,o):e.get(o),i=zn(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=Gn(await Yn(s,rt(n,t),e.get(o)),i.format);r.json(d)}catch(s){if(s instanceof ye){$e(r,404,s.code);return}if(s instanceof te||s instanceof je||s instanceof Ze){$e(r,s.statusCode,s.code);return}let i=J(s,"rest");if(i){r.status(i.statusCode).json(i.body);return}z(r,s);return}}}a(Uc,"getProposalDiffHandler");function Bc(e,t,n,r,o){return async(s,i)=>{if(!s.accessLink){$e(i,401,"access_link_missing");return}try{let d=Z(s,r),l=await $o(e,t,s.accessLink,String(s.params.id),s.body??{},n,d,o);i.json(be(e,s.accessLink,l,d))}catch(d){z(i,d)}}}a(Bc,"resolveProposalHandler");function qc(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=We(e,u,p).proposal;t.append("proposal.conflict_resolved",tr(o),{proposalId:l.id,nodeId:c.nodeId,choice:"undo"}),s.json(ue(f,Z(o,r)));return}let d=await _i(e,t,tr(o),String(o.params.id),i.nodeId,i.choice,n);s.json(ue(d,Z(o,r)))}catch(i){z(s,i)}}}a(qc,"resolveProposalConflictHandler");function Wc(e,t,n){return async(r,o)=>{try{se(e);let s=e.get(String(r.params.id)),i=await dt(t);i&&U(s.status)&&(s=We(e,s,i).proposal),o.json(ue(s,Z(r,n)))}catch(s){z(o,s)}}}a(Wc,"recheckProposalHandler");Ne();y();y();Ee();y();Ee();mo();function _y(e){return e.endsWith("/")?e.slice(0,-1):e}a(_y,"trimTrailingSlash");var ws=class{static{a(this,"HttpAdapter")}kind="http";baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=_y(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 te(n):t.status===409?new re("unknown","unknown",`Admin API ${n}`):t.status===401||t.status===403?new Ze(io):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 yr(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 te(t);let s=await this.readJson(r,"GET /flows"),i=await this.readJson(o,`GET /flow/${t}`);return{rev:yr(s,"GET /flows").rev,flow:_n(t,i)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new re(t.expectedRev,r.rev,"updateFlow");let o=ho(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 re(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}}};y();Ee();mo();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 Ey(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(Ey,"resolveRev");var bs=class{static{a(this,"RedAdapter")}kind="red";flows;constructor(t){this.flows=Cy(t)}async getFlows(){let t=await this.flows.getFlows({});return yr(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 Iy(o)?new te(t):o}return{rev:n.rev,flow:_n(t,r)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new re(t.expectedRev,r.rev,"updateFlow");let o=ho(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 re(t.expectedRev,d.rev,"updateFlow")}throw i}return{flowId:t.flowId,rev:Ey(s)}}};function zc(e){let t=e?.runtime?.flows;return!!(t?.getFlows&&t.getFlow&&t.setFlows)}a(zc,"canUseRedRuntime");function Iy(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(Iy,"isRuntimeFlowNotFound");y();var Gc=require("module");function Kc(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(Kc,"isFlowsApi");function Ly(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Gc.createRequire)(e)}a(Ly,"createPluginRequire");function Ny(e){try{let t=e("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}a(Ny,"isNodeRedRuntimeInitialized");function Fy(){try{let e=Ly();if(!Ny(e))return null;let t=e("@node-red/runtime");return Kc(t.flows)?t.flows:null}catch{return null}}a(Fy,"tryRequireInitializedRuntimeFlows");function Jc(e){if(e?.runtime&&Kc(e.runtime.flows))return e;let t=Fy();return t?{runtime:{flows:t}}:null}a(Jc,"resolveRuntimeHost");function Vc(e={}){let t=Jc(e.RED??null);if(t&&zc(t))return new bs(t);let n=e.adminAuthEnabled===!0;if(e.http?.baseUrl){if(n&&!e.http.accessToken)throw new je("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 ws(e.http)}throw new je("No usable Node-RED flow adapter: in-process flows API is unavailable and no HTTP Admin API baseUrl was provided")}a(Vc,"createFlowAdapter");Ee();Us();y();var ks=require("fs"),Yc=require("path");co();de();var Oy=/^[a-z][a-z0-9._-]{0,63}$/;function $y(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($y,"sanitizeActor");var vs=class{constructor(t,n={}){this.paths=t;this.filePath=(0,Yc.join)(t.logs,"audit.jsonl"),this.now=n.now??(()=>Date.now()),this.maxAppendBytes=n.maxAppendBytes??ha}static{a(this,"AuditLog")}filePath;now;maxAppendBytes;seq=0;append(t,n,r){let o=Oy.test(t)?t:"unknown";this.seq+=1;let s={id:`log-${this.now()}-${this.seq}`,at:new Date(this.now()).toISOString(),action:o,actor:$y(n)};r!==void 0&&(s.details=La(r));let i=St(s,this.maxAppendBytes,{kind:"audit"});return ga(this.paths.root,this.filePath,i),s}list(t=100){if(!(0,ks.existsSync)(this.filePath))return[];let n=(0,ks.readFileSync)(this.filePath,"utf8").split(`
7191
+ `).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()}};tt();Qr();Re();Zt();Bo();Te();function My(e){let t=a(()=>Promise.reject(new Ze(e)),"fail");return{kind:"http",getFlows:t,getFlow:t,updateFlow:t}}a(My,"misconfiguredAdapter");function Zc(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(Zc,"localAdminBaseUrl");function As(e,t={}){let n=Kr(e.settings.userDir);ya(n);let r=t.settingsStore??new An(n,e.settings.aiAccess,u=>e.log?.warn?.(u));t.bindSettings!==!1&&to(r),ro(r.getEffective().publicBaseUrl);let o=t.linkStore??hs(e);o.configureDefaults({defaultTtlSeconds:r.getEffective().defaultLinkTtlSeconds,maxActiveLinks:r.getEffective().maxActiveLinks});let s=new po(n),i=new vs(n),d;if(t.flowAdapter)d=t.flowAdapter;else try{d=Vc({RED:e,http:{baseUrl:Zc(e)},adminAuthEnabled:ae(e.settings)})}catch(u){let p=u instanceof je?u.message:io;e.log?.warn?.(`AI Access flow adapter unavailable: ${p}`),d=My(p)}d=Ma(d);let l;if(t.nodeTypesAdapter)l=t.nodeTypesAdapter;else if(t.flowAdapter)l=Ks();else try{l=Ga({baseUrl:Zc(e)})}catch(u){let p=u instanceof Error?u.message:String(u);e.log?.warn?.(`AI Access node-types adapter unavailable: ${p}`),l=Ks([])}let c=new Uo(s,i,d,()=>Me(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(As,"createPluginServices");y();de();Re();Ye();Jr();y();var Xc=rr(require("dns"),1),Qc=rr(require("http"),1),eu=rr(require("https"),1),qr=require("net");Re();function Dy(e,t){return e===10||e===172&&t>=16&&t<=31||e===192&&t===168}a(Dy,"isPrivateV4");function Hy(e,t){return e===169&&t===254}a(Hy,"isLinkLocalV4");function jy(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(jy,"isSpecialUseV4");function Uy(e){let[t=0,n=0,r=0]=e.split(".").map(Number);return t===127?"loopback":Dy(t,n)||Hy(t,n)||jy(t,n,r)||e==="168.63.129.16"?"blocked":"public"}a(Uy,"classifyIpv4");function By(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(By,"expandIpv6");function qy(e){let t=By(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(qy,"classifyIpv6");function Wr(e){let t=Kt(e);return t?(0,qr.isIP)(t)===4?Uy(t):qy(t):"blocked"}a(Wr,"classifyPublicUrlAddress");function Wy(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(Wy,"isMetadataHostname");async function tu(e,t,n){let r=xn(e);if(!r)throw new Error("publicBaseUrl is required for Check");let o=new URL(r),s=o.hostname.replace(/^\[|\]$/g,"");if(Wy(s))throw new Error("Public URL Check cannot target metadata hosts");let i=Kt(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&&Wr(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 w=Wr(g);if(w==="blocked"||w==="loopback"&&!c||c&&w!=="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,qr.isIP)(i)};let f=await new Promise((g,w)=>{let v=a(()=>w(n.reason instanceof Error?n.reason:new Error(String(n.reason))),"abort");n.throwIfAborted(),n.addEventListener("abort",v,{once:!0}),Xc.default.lookup(s,{all:!0},(k,$)=>{n.removeEventListener("abort",v),k?w(k):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 w=Kt(g.address);if(!w)throw new Error("Public URL Check returned an invalid address");h.some(v=>v.address===w)||h.push({address:w,family:(0,qr.isIP)(w)})}c&&h.sort((g,w)=>g.family-w.family);let m=h[0];if(!m)throw new Error("Public URL Check hostname has no addresses");return{origin:r,...m,addresses:h}}a(tu,"preparePublicUrlCheck");function zy(e,t,n){let r=e.hostname.replace(/^\[|\]$/g,"");return new Promise((o,s)=>{let d=(e.protocol==="https:"?eu.default:Qc.default).request(e,{method:"GET",agent:!1,signal:n,headers:{Host:e.host},...(0,qr.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(zy,"requestPinned");async function nu(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 zy(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(nu,"requestPublicUrlCheck");vn();Te();function Gy(e){return ur(e.settings.httpAdminRoot)}a(Gy,"mcpPageUrl");function Ky(e){return typeof e=="boolean"||typeof e=="number"||typeof e=="string"?e:Array.isArray(e)?e.join(", "):typeof e=="function"?"custom function":!1}a(Ky,"normalizeTrustProxy");function su(e){if(!e)return"Set a Public URL before enabling OAuth.";let t=new URL(e),n=Kt(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(su,"oauthPublicUrlWarning");function iu(e,t,n){let{sources:r,...o}=t.getEffective(),{permission:s}=no(e,n);return{ok:!0,adminAuthEnabled:ae(e.settings),canEdit:s==="*",permission:s,mcpPageUrl:Gy(e),apiBase:_e(e.settings.httpAdminRoot),settings:o,oauthPublicUrlWarning:su(o.publicBaseUrl),sources:r,disk:t.getDisk(),trustProxy:Ky(n.app.get("trust proxy")),precedence:["settings.js","disk","default"],defaults:{secretNamePatterns:[...Wt]}}}a(iu,"settingsResponse");function au(e,t){return(n,r)=>{r.json(iu(e,t,n))}}a(au,"getSettingsHandler");function ru(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(ru,"parseBooleanField");function ou(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(ou,"parsePositiveIntField");function Jy(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(Jy,"parseRateLimitField");function Vy(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=>ir(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(Vy,"parseCorsField");function Yy(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>ar)throw new Error(`secretNamePatterns allows at most ${ar} entries`);for(let o of r)if(!Vr.test(o.trim()))throw new Error('secretNamePatterns entries must be 2-40 letters, digits, "_" or "-" (e.g. API_KEY)');t.secretNamePatterns=dr(r)??[]}}a(Yy,"parseSecretPatternsField");function du(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}ru(i,"enabled",d),ru(i,"allowWithoutAdminAuth",d),ou(i,"defaultLinkTtlSeconds",d),ou(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=xn(i.publicBaseUrl)),Vy(i,d),Yy(i,d,l);for(let c of["rateLimitWindowMs","rateLimitMaxPerIp","rateLimitMaxPerToken"])Jy(i,c,d)}catch(c){o.status(400).json({error:c.message,code:"invalid_body"});return}try{let c=t.patch(d,l);ro(c.publicBaseUrl),n.configureDefaults({defaultTtlSeconds:c.defaultLinkTtlSeconds,maxActiveLinks:c.maxActiveLinks});let u=iu(e,t,r),p=c.publicBaseUrl?new URL(c.publicBaseUrl):void 0,f=p?Kt(p.hostname.replace(/^\[|\]$/g,"")):void 0,h=f?Wr(f):void 0,m=[];h!==void 0&&h!=="public"&&m.push("Public URL uses a private or non-public IP address.");let g=su(c.publicBaseUrl);g&&c.publicBaseUrl&&m.push(g),o.json({...u,...m.length?{warnings:m}:{}})}catch(c){if(c instanceof he){o.status(c.statusCode).json({error:c.message,code:c.code});return}z(o,c);return}}}a(du,"putSettingsHandler");function lu(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"||Wr(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 tu(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+_e(e.settings.httpAdminRoot);let d=await nu(i,_e(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(lu,"checkPublicUrlHandler");Te();Gt();Te();le();Qr();var Zy="com55-ai-access",xs=new WeakMap;function uu(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(uu,"acceptedMediaTypes");function pu(e){let t=uu(e.headers.accept);return t.has("text/event-stream")&&!t.has("text/html")}a(pu,"isMcpStreamRequest");function Xy(e,t,n){e.httpAdmin.get("/mcp/api/v1",t,n,Ni(e)),e.httpAdmin.get("/mcp/api/v1/",t,n,Ni(e)),e.httpAdmin.get("/mcp/api/v1/openapi.yaml",t,n,_c()),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",Wn),o.type("application/javascript").status(200).send(qo())})}a(Xy,"registerMcpShellAndDiscovery");var Qy=/^\/mcp\/(?:api\/v1\/proposals|rpc$|a\/[^/]+\/(?:proposals|propose|draft))/;function fu(e,t={}){let n=As(e,{...t,linkStore:t.linkStore??hs(e)});$d(()=>n.settingsStore.getEffective().proposalTtlDays),xs.get(e)?.();let r=Dd(()=>_r(n.proposalStore,n.flowAdapter),500),o=a(A=>{A?.config?.rev&&Array.isArray(A.config.flows)&&Ts(we(n.flowAdapter),A.config)},"observeRuntimeDeploy"),s=a(()=>{r.cancel(),e.events?.removeListener?.("flows:stopping",o),e.events?.removeListener?.("flows:started",o),e.events?.removeListener?.("flows:started",r.trigger)},"cleanup");xs.set(e,s),e.events?.on("flows:stopping",o),e.events?.on("flows:started",o),e.events?.on("flows:started",r.trigger);let i=yl(()=>n.settingsStore.getEffective()),{ip:d,token:l}=i,c=new gs(Kr(e.settings.userDir),()=>n.settingsStore.getEffective().oauth.dcrMaxClients),u=a(()=>n.settingsStore.getEffective().publicBaseUrl,"publicBase"),p=a(()=>`${u().replace(/\/$/,"")}/mcp`,"mcpUrl"),f=new ys(c,p,()=>n.settingsStore.getEffective().oauth.accessTokenTtlSec,()=>n.settingsStore.getEffective().oauth.refreshIdleTtlSec,()=>n.settingsStore.getEffective().oauth.refreshMaxLifetimeSec),h=Sc(e,f,u,p);ba(e.httpAdmin,wa(()=>n.settingsStore.getEffective().corsAllowedOrigins));let m=a(A=>(Array.isArray(A)?A:A?[A]:[]).flatMap(I=>{if(typeof I=="string")return[I];let b=I;return b&&typeof b.path=="string"?[{path:b.path,root:typeof b.root=="string"?b.root:void 0}]:[]}),"staticMounts"),g={authorization:[],metadata:[],details:[],httpNodeRootFallback:e.settings.httpNodeRoot===!1||!e.httpNode},w="",v=a(()=>{let A=xc({httpAdmin:e.httpAdmin,httpNode:e.httpNode,httpAdminRoot:e.settings.httpAdminRoot,httpNodeRoot:e.settings.httpNodeRoot,metadataFallback:g.httpNodeRootFallback,eachNode:e.nodes?L=>e.nodes?.eachNode(L):void 0,staticDirs:m(e.settings.httpStatic),httpStaticRoot:e.settings.httpStaticRoot,fileExists:cu.existsSync}),I=A.authorization,b=A.metadata;g.authorization=I.map(oa),g.metadata=b.map(oa),g.details=[...I.map(L=>({...L,kind:"authorization"})),...b.map(L=>({...L,kind:"metadata"}))];let M=[...g.authorization.map(L=>`${L} \u2014 OAuth authorization routes disabled`),...g.metadata.map(L=>`${L} \u2014 OAuth metadata disabled`)],H=M.join(`
7192
+ `);if(H!==w)for(let L of M)e.log?.warn?.(`AI Access OAuth route collision: ${L}`);w=H},"refreshCollisions");v(),e.events?.on("flows:started",v);let k=Mi({windowMs:6e4,maxPerIp:30}),$=a((A,I,b)=>{if(!n.settingsStore.getEffective().oauth.enabled||g.authorization.length||!A.path.startsWith("/mcp/oauth/")){b();return}let M=A.path.replace(/\/+$/,"").toLowerCase(),H=a(()=>Sn(e)(A,I,()=>h.server(A,I,b)),"run");M==="/mcp/oauth/authorize"?d(A,I,()=>k(A,I,H)):M.startsWith("/mcp/oauth/consent/")?d(A,I,H):H()},"oauthServerMiddleware");$.oauthOwned=!0,e.httpAdmin.use($);let C=a((A,I,b)=>{if(!n.settingsStore.getEffective().oauth.enabled||g.metadata.length){b();return}h.metadata(A,I,b)},"metadataHandler");C.oauthOwned=!0,g.httpNodeRootFallback?e.httpAdmin.use("/mcp",C):e.httpNode?.use(C),e.httpAdmin.get("/mcp/api/v1/oauth/clients",d,l,Ce(e,"api"),(A,I)=>I.json({clients:c.data.clients.map(b=>({client_id:b.client_id,client_name:b.client_name,redirect_uris:b.redirect_uris,lastUsedAt:b.lastUsedAt}))})),e.httpAdmin.get("/mcp/api/v1/oauth/status",d,l,Ce(e,"api"),(A,I)=>{let b=!ae(e.settings)||Tt(A)==="*";I.json({...g,details:b?g.details:g.details.map(({file:M,...H})=>H),collisions:[...g.authorization,...g.metadata],oauthEnabled:n.settingsStore.getEffective().oauth.enabled})}),e.httpAdmin.delete("/mcp/api/v1/oauth/clients/:id",d,l,Ce(e,"api"),Rn(e),(A,I)=>I.json({revoked:c.revokeClient(String(A.params.id))})),e.httpAdmin.use((A,I,b)=>{if(!Qy.test(A.path)){b();return}n.flowAdapter.getFlows().then(M=>td(A,ko(M.flows))).catch(()=>{}).finally(()=>b())}),e.httpAdmin.use(ic(n.auditLog)),Ul(e,{linkStore:n.linkStore,proposalStore:n.proposalStore,auditLog:n.auditLog,flowAdapter:Lt(n.flowAdapter),nodeTypesAdapter:n.nodeTypesAdapter,...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{},rateLimiters:i}),e.httpAdmin.get("/ai-access",d,l,(A,I)=>{I.append("Content-Security-Policy","form-action 'none'; frame-ancestors 'none'"),I.setHeader("X-Frame-Options","DENY"),I.setHeader("Cache-Control","no-store, no-cache, must-revalidate, max-age=0"),I.setHeader("Pragma","no-cache"),I.setHeader("Expires","0"),I.type("html").status(200).send(Nc({apiBase:_e(e.settings.httpAdminRoot),mcpRpc:Rt(e.settings.httpAdminRoot),editorRoot:Pe(e.settings.httpAdminRoot),authTokenKey:so(e.settings.httpAdminRoot),dashboardPath:ur(e.settings.httpAdminRoot)}))}),e.httpAdmin.get("/mcp",d,(A,I,b)=>{if(pu(A)){b();return}I.setHeader("Cache-Control","no-store"),I.redirect(302,ur(e.settings.httpAdminRoot))}),Xy(e,d,l),e.httpAdmin.get("/mcp/api/v1/status",d,l,Ra(e,A=>n.linkStore.validateResult(A)),D((A,I)=>{I.json(Zn(e,A))})),e.httpAdmin.get("/mcp/api/v1/settings",d,l,Ce(e,"api"),D(au(e,n.settingsStore))),e.httpAdmin.put("/mcp/api/v1/settings",d,l,Ce(e,"api"),Rn(e),Fe("rest"),D(du(e,n.settingsStore,n.linkStore))),e.httpAdmin.post("/mcp/api/v1/settings/check-public-url",d,l,Ce(e,"api"),Rn(e),Fe("rest"),D(lu(e)));let O=[d,l,Ce(e,"api"),Sn(e),Rn(e)];e.httpAdmin.get("/mcp/api/v1/links",...O,D(Yl(e,n.linkStore))),e.httpAdmin.post("/mcp/api/v1/links",...O,D(Zl(e,n.linkStore,n.auditLog))),e.httpAdmin.delete("/mcp/api/v1/links/:id",...O,D(Xl(e,n.linkStore,n.auditLog)));let q=[d,l,Sn(e),Sa(e,A=>n.linkStore.validateResult(A))],T=n.flowAdapter,S=n.nodeTypesAdapter,F={nodeTypes:S,...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{}};e.httpAdmin.get("/mcp/api/v1/flows",...q,D(al(T))),e.httpAdmin.get("/mcp/api/v1/flows/:id",...q,D(ts(T))),e.httpAdmin.get("/mcp/api/v1/nodes/:id",...q,D(ns(T))),e.httpAdmin.get("/mcp/api/v1/nodes/:id/field/:name",...q,D(rs(T))),e.httpAdmin.get("/mcp/api/v1/search",...q,D(os(T))),e.httpAdmin.get("/mcp/api/v1/node-types",...q,D(ss(F))),e.httpAdmin.get("/mcp/api/v1/node-types/:type",...q,D(is(F))),e.httpAdmin.get("/mcp/api/v1/palette/search",...q,D(as(F)));let N=e.settings.httpAdminRoot;e.httpAdmin.get("/mcp/api/v1/proposals",...q,D(Oc(n.proposalStore,n.flowAdapter,N))),e.httpAdmin.get("/mcp/api/v1/proposals/:id",...q,D($c(n.proposalStore,N,n.flowAdapter))),e.httpAdmin.get("/mcp/api/v1/proposals/:id/diff",...q,D(Uc(n.proposalStore,n.flowAdapter))),e.httpAdmin.post("/mcp/api/v1/proposals",d,l,Fe("rest"),Pn(e,n.linkStore,"rest"),D(Fc(n.proposalStore,n.auditLog,n.flowAdapter,N,n.nodeTypesAdapter))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/approve",...O,Fe("rest"),D(Mc(n.proposalService,N))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/confirm-live-checked",...O,Fe("rest"),D(Dc(n.proposalService,N))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/reject",...O,Fe("rest"),D(Hc(n.proposalService,N))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/withdraw",...q,(A,I,b)=>{A.accessLink?b():Ce(e,"api")(A,I,()=>Rn(e)(A,I,b))},Fe("rest"),D(jc(n.proposalStore,n.auditLog,N))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/resolve-conflict",...O,Fe("rest"),D(qc(n.proposalStore,n.auditLog,T,N))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/recheck",...O,D(Wc(n.proposalStore,T,N))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/resolve",d,l,Fe("rest"),Pn(e,n.linkStore,"rest"),D(Bc(n.proposalStore,n.auditLog,T,N,S)));let P=[d,l,(A,I,b)=>{let M=A.headers.authorization;if(!n.settingsStore.getEffective().oauth.enabled||M?.toLowerCase().startsWith("bearer aia_")){if(n.settingsStore.getEffective().oauth.enabled){let H=I.setHeader.bind(I);I.setHeader=((L,ee)=>{if(L.toLowerCase()==="www-authenticate"&&typeof ee=="string"&&!ee.includes("resource_metadata=")){let Se=`${u().replace(/\/$/,"")}${g.httpNodeRootFallback?"/mcp":""}/.well-known/oauth-protected-resource/mcp`;ee+=`, resource_metadata="${Se}", scope="read propose"`}return H(L,ee)})}Pn(e,n.linkStore,"mcp")(A,I,b);return}Sn(e)(A,I,()=>h.auth(A,I,b))}];e.httpAdmin.post("/mcp/rpc",...P,Fe("mcp"),D(Zi(e,n))),e.httpAdmin.get("/mcp/rpc",...P,jr),e.httpAdmin.delete("/mcp/rpc",...P,jr),e.httpAdmin.post("/mcp",...P,Fe("mcp"),D(Zi(e,n))),e.httpAdmin.get("/mcp",...P,jr),e.httpAdmin.delete("/mcp",...P,jr),e.httpAdmin.get("/mcp/api/v1/logs",d,l,Ce(e,"api"),Sn(e),D(Ql(n.auditLog,n.linkStore))),e.httpAdmin.use("/mcp/api/v1",ol()),zl(e)}a(fu,"registerRoutes");function ew(e){e.plugins.registerPlugin(Zy,{type:"com55-ai-access",onremove(){xs.get(e)?.(),xs.delete(e)},onadd(){fu(e),e.log?.info?.("Node-RED AI Access plugin loaded (dashboard /ai-access, MCP /mcp)")}})}a(ew,"registerAiAccessPlugin");var tw=ew;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});