@com55/node-red-ai-access 0.2.0-beta.4 → 0.2.0-beta.5

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,12 +1,15 @@
1
- "use strict";var kd=Object.create;var dn=Object.defineProperty;var Ad=Object.getOwnPropertyDescriptor;var xd=Object.getOwnPropertyNames;var Sd=Object.getPrototypeOf,Td=Object.prototype.hasOwnProperty;var a=(e,t)=>dn(e,"name",{value:t,configurable:!0});var U=(e,t)=>()=>(e&&(t=e(e=0)),t);var ds=(e,t)=>{for(var n in t)dn(e,n,{get:t[n],enumerable:!0})},ls=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of xd(t))!Td.call(e,o)&&o!==n&&dn(e,o,{get:()=>t[o],enumerable:!(r=Ad(t,o))||r.enumerable});return e};var ln=(e,t,n)=>(n=e!=null?kd(Sd(e)):{},ls(t||!e||!e.__esModule?dn(n,"default",{value:e,enumerable:!0}):n,e)),Pd=e=>ls(dn({},"__esModule",{value:!0}),e);function Be(e,t,n){let r=n.pretty?`${JSON.stringify(e,null,2)}
1
+ "use strict";var kl=Object.create;var gn=Object.defineProperty;var Al=Object.getOwnPropertyDescriptor;var Sl=Object.getOwnPropertyNames;var xl=Object.getPrototypeOf,Tl=Object.prototype.hasOwnProperty;var a=(e,t)=>gn(e,"name",{value:t,configurable:!0});var U=(e,t)=>()=>(e&&(t=e(e=0)),t);var Os=(e,t)=>{for(var n in t)gn(e,n,{get:t[n],enumerable:!0})},$s=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of Sl(t))!Tl.call(e,o)&&o!==n&&gn(e,o,{get:()=>t[o],enumerable:!(r=Al(t,o))||r.enumerable});return e};var yn=(e,t,n)=>(n=e!=null?kl(xl(e)):{},$s(t||!e||!e.__esModule?gn(n,"default",{value:e,enumerable:!0}):n,e)),Rl=e=>$s(gn({},"__esModule",{value:!0}),e);function Je(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 X(n.kind,o.byteLength,t);return{bytes:o}}function us(e,t){let n=(0,cn.resolve)(t),r=(0,cn.resolve)(e);if(r!==n&&!r.startsWith(n+cn.sep))throw new Error("Path escapes .ai-access storage root");return r}function Rt(e,t,n){let r=us(t,e),o=`${r}.${process.pid}.tmp`;(0,Me.writeFileSync)(o,n.bytes,{mode:Hn}),(0,Me.renameSync)(o,r),(0,Me.chmodSync)(r,Hn)}function ps(e,t,n){let r=us(t,e);(0,Me.appendFileSync)(r,n.bytes,{mode:Hn});try{(0,Me.chmodSync)(r,Hn)}catch{}}var Me,cn,Yr,Hn,j,cs,X,G=U(()=>{"use strict";Me=require("fs"),cn=require("path"),Yr=448,Hn=384,j=15e5,cs=8192,X=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(Be,"encodeTrustedJson");a(us,"assertUnderAiAccess");a(Rt,"writeTrustedAtomic");a(ps,"appendTrusted")});function pn(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 Ed(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 ms(e){return(t,n,r)=>{if(!Rd.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=pn(t.get("origin")),d=i===void 0?void 0:e().find(c=>c===i),l=d!==void 0;if(Ed(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",Cd),t.method==="OPTIONS"&&t.get("access-control-request-method")){n.setHeader("Access-Control-Allow-Methods",_d),n.setHeader("Access-Control-Allow-Headers",Ld),n.setHeader("Access-Control-Max-Age",String(600)),n.status(204).end();return}s=!1,r()}}function hs(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 Rd,_d,Ld,Cd,qn=U(()=>{"use strict";Rd=/^\/mcp(?:\/?$|\/(?:a(?:\/|$)|api(?:\/|$)|rpc\/?$))/,_d="GET, POST, DELETE, OPTIONS",Ld="Authorization, Content-Type, Accept, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID",Cd="Mcp-Session-Id, WWW-Authenticate, Retry-After, RateLimit, RateLimit-Policy, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset";a(pn,"canonicalOrigin");a(Ed,"appendVary");a(ms,"aiCorsMiddleware");a(hs,"installAiCorsMiddleware")});function Nd(e){return e==="nodes"||e==="flows"||e==="full"}function Wn(e,t){if(!e||typeof e!="object"||Array.isArray(e))return{};let n=e,r={};if(typeof n.enabled=="boolean"&&(r.enabled=n.enabled),typeof n.allowWithoutAdminAuth=="boolean"&&(r.allowWithoutAdminAuth=n.allowWithoutAdminAuth),typeof n.defaultLinkTtlSeconds=="number"&&Number.isFinite(n.defaultLinkTtlSeconds)&&n.defaultLinkTtlSeconds>0&&(r.defaultLinkTtlSeconds=Math.floor(n.defaultLinkTtlSeconds)),Nd(n.defaultDeploymentType)&&(r.defaultDeploymentType=n.defaultDeploymentType),typeof n.maxActiveLinks=="number"&&Number.isFinite(n.maxActiveLinks)&&n.maxActiveLinks>0&&(r.maxActiveLinks=Math.floor(n.maxActiveLinks)),typeof n.publicBaseUrl=="string")try{r.publicBaseUrl=Ct(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=pn(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.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 Fd(e,t){if(!(0,mn.existsSync)(e))return{};try{let n=(0,mn.readFileSync)(e,"utf8");return Wn(JSON.parse(n),t)}catch(n){return t?.(`AI Access: config.json is corrupt or unreadable; defaults are used (${String(n)})`),{}}}function zn(e,t){let n={},r={...fn};for(let s of Object.keys(fn).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]??fn[s]});let o={...r,sources:n};return e.oauth!==void 0||t.oauth!==void 0?(o.oauth={..._t,...t.oauth??{},...e.oauth??{}},n.oauth=e.oauth!==void 0?"settings.js":"disk"):(o.oauth={..._t},n.oauth="default"),o}function eo(e,t){let n=Wn(e??{},t);for(let o of Object.keys(fn).filter(s=>s!=="oauth"))e&&Object.hasOwn(e,o)&&n[o]===void 0&&Object.assign(n,{[o]:fn[o]});let r=e?.oauth;if(r&&typeof r=="object"&&!Array.isArray(r)){let o=n.oauth??={};for(let s of Object.keys(_t))Object.hasOwn(r,s)&&o[s]===void 0&&(t?.(`AI Access: settings.js aiAccess.oauth.${s} is invalid; using the default (${String(_t[s])})`),Object.assign(o,{[s]:_t[s]}))}return n}var mn,Zr,Id,_t,fn,Lt,Gn=U(()=>{"use strict";mn=require("fs"),Zr=require("path");ie();qn();G();Id=16384,_t={enabled:!1,accessTokenTtlSec:3600,refreshIdleTtlSec:2592e3,refreshMaxLifetimeSec:7776e3,dcrMaxClients:5},fn={enabled:!0,allowWithoutAdminAuth:!1,defaultLinkTtlSeconds:14400,maxActiveLinks:10,defaultDeploymentType:"nodes",publicBaseUrl:"",corsAllowedOrigins:[],rateLimitWindowMs:6e4,rateLimitMaxPerIp:600,rateLimitMaxPerToken:300,oauth:{..._t}};a(Nd,"isDeploymentType");a(Wn,"sanitizePartial");a(Fd,"readDiskFile");a(zn,"mergeAiAccessSettings");a(eo,"settingsJsPartial");Lt=class{constructor(t,n,r){this.paths=t;this.filePath=(0,Zr.join)(t.root,"config.json"),this.settingsJs=eo(n,r),this.disk=Fd((0,mn.existsSync)(this.filePath)?this.filePath:(0,Zr.join)(t.root,"settings.json"),r),this.cached=zn(this.settingsJs,this.disk)}static{a(this,"SettingsStore")}filePath;disk;settingsJs;cached;getEffective(){return this.cached}getDisk(){return{...this.disk}}update(t){let n=Wn(t);for(let r of Object.keys(this.settingsJs))delete n[r];return this.persist(n),this.disk=n,this.cached=zn(this.settingsJs,this.disk),this.cached}patch(t){let n=Wn(t);return this.update({...this.disk,...n,...n.oauth?{oauth:{...this.disk.oauth,...n.oauth}}:{}})}persist(t){let n=Be(t,Id,{kind:"settings",pretty:!0});Rt(this.paths.root,this.filePath,n)}}});function Jn(e){to=e}function W(e){return e.adminAuth!=null&&e.adminAuth!==!1}function he(e){return to?to.getEffective():zn(eo(e.aiAccess),{})}function ge(e){let t=he(e);return t.enabled?W(e)?!0:t.allowWithoutAdminAuth:!1}var to,ye=U(()=>{"use strict";Gn();to=null;a(Jn,"bindSettingsStore");a(W,"isAdminAuthEnabled");a(he,"resolveAiAccessSettings");a(ge,"areAiFeaturesEnabled")});function ae(e){let t=`/${e??""}`.replace(/\/{2,}/g,"/");for(;t.length>1&&t.endsWith("/");)t=t.slice(0,-1);return t}function de(e){let t=ae(e);return t==="/"?"/mcp/api/v1":`${t}/mcp/api/v1`}function hn(e){let t=ae(e);return t==="/"?"/ai-access":`${t}/ai-access`}function qe(e){let t=ae(e);return t==="/"?"/mcp/rpc":`${t}/mcp/rpc`}function Od(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 We(e){if(e.auth)return e.auth.scopes.includes("propose")?"propose":e.auth.scopes.includes("read")?"read":null;let t=Od(e);return t.includes("*")?"*":t.includes("read")?"read":null}function $d(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 gs(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 we(e,t){let n="read";return(r,o,s)=>{if(!W(e.settings)){s();return}let i=t==="api"?gs(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(!We(r)){o.status(401).json({error:"Insufficient permissions",code:"insufficient_permissions"});return}s()})}}function Kn(e,t){let n=W(e.settings),r=he(e.settings),o=ge(e.settings),s=null;return n?s=We(t):o&&(s="*"),{adminAuthEnabled:n,aiFeaturesEnabled:o,allowWithoutAdminAuth:r.allowWithoutAdminAuth,permission:s,capabilities:$d(s,o)}}function Et(e){return(t,n,r)=>{if(!ge(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 gn(e){return(t,n,r)=>{if(!W(e.settings)){if(ge(e.settings)){r();return}n.status(403).json({error:"AI Access features are disabled",code:"ai_features_disabled"});return}if(We(t)!=="*"){n.status(403).json({error:"Full admin permission (*) is required for this action",code:"insufficient_permissions"});return}r()}}function Md(e){return e.startsWith("aia_")}function Dd(e){return Md(e)?!0:e.length<24}function jd(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 ys(e,t,n,r){if(!W(e.settings)){r();return}let o={error:le,code:Le},s=gs(n,o,Y);e.auth.needsPermission("read")(t,n,i=>{if(s(),i){r(i);return}if(!n.headersSent){if(!t.user||!We(t)){n.setHeader("WWW-Authenticate",Y),n.status(401).json(o);return}r()}})}function ws(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?(bn(t,r,l.link.kind),"wrong_kind"):(e.accessLink=l.link,"ok");if(l.status==="expired")return wn(e,t,o,l.expiresAt),"invalid";if(Dd(d)){t.setHeader("WWW-Authenticate",Y);let c=jd(d);return t.status(401).json({error:st,code:yn,...c?{hint:c}:{}}),"invalid"}return"continue"}function bs(e,t,n="rest"){return(r,o,s)=>{if(!ge(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=ws(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||ys(e,r,o,s)}}function vs(e,t,n="rest"){return(r,o,s)=>{let i=ws(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||ys(e,r,o,s)}}var _e=U(()=>{"use strict";it();ye();a(ae,"normalizeHttpAdminRoot");a(de,"mcpApiBase");a(hn,"dashboardPath");a(qe,"mcpRpcPath");a(Od,"scopesFromRequest");a(We,"resolvePermission");a($d,"capabilitiesFor");a(gs,"interceptNeedsPermissionUnauthorized");a(we,"requireAdminSession");a(Kn,"describeAccess");a(Et,"requireAiFeatures");a(gn,"requireStarPermission");a(Md,"looksLikeAccessLinkToken");a(Dd,"looksLikeFailedAccessLinkAttempt");a(jd,"truncatedTokenHint");a(ys,"continueAsAdminSessionOrAccessLinkMissing");a(ws,"tryAccessLinkBearer");a(bs,"requireAdminReadOrAccessLink");a(vs,"requireAdminSessionOrAccessLink")});function Hd(e,t){let n=ae(e);return`${n==="/"?"/mcp/a":`${n}/mcp/a`}/${t}`}function Ud(e){return!!e.app.get("trust proxy")}function ks(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 Bd(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 at(e){try{let n=new URL(`http://${e.includes(":")&&!e.startsWith("[")?`[${e}]`:e}`).hostname.replace(/^\[|\]$/g,"");if(!(0,no.isIP)(n))return;if(n.startsWith("::ffff:")){let r=n.slice(7);if((0,no.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 Ct(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 Vn(e){ro=Bd(e)}function xs(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],!zd.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)||!Wd.test(t)&&!qd.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 Ts(e){let t=e?.trim().toLowerCase();return t==="http"||t==="https"?t:void 0}function Gd(e){let t=e.socket;return t?.encrypted?"https":t?"http":Ts(e.protocol)??"http"}function oo(e){if(ro)return ro;let t=Ud(e),n=t?ks(e,"x-forwarded-host"):void 0,r=e.get("host")?.trim(),o=xs(n)?n:xs(r)?r:void 0;if(!o)return;let s=(t?Ts(ks(e,"x-forwarded-proto")):void 0)??Gd(e);try{return new URL(`${s}://${o}`).origin}catch{return}}function vn(e,t){let n=oo(e);return n?`${n}${t}`:t}function Ps(e,t,n){return vn(e,Hd(t,n))}function so(e){return e.replace(/\/mcp\/a\/[^/?#]+/gi,"/mcp/a/<redacted>")}var no,It,Ss,ro,As,qd,Wd,zd,ie=U(()=>{"use strict";no=require("net");_e();It=8192,Ss=1800*1e3;a(Hd,"mcpAccessLinkPath");a(Ud,"isTrustProxyEnabled");a(ks,"firstForwardedValue");a(Bd,"normalizePublicBaseUrl");a(at,"canonicalizeIp");a(Ct,"validateSettingsPublicBaseUrl");a(Vn,"bindPublicBaseUrl");As="[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?",qd=new RegExp(`^${As}(?:\\.${As})*$`),Wd=/^(?:(?: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)$/,zd=/^\[[0-9A-Fa-f:.]+\]$/;a(xs,"isValidHostHeader");a(Ts,"validScheme");a(Gd,"connectionScheme");a(oo,"requestPublicOrigin");a(vn,"absolutizePath");a(Ps,"buildAccessLinkUrl");a(so,"redactAccessLinkTokenInPath")});function Qd(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 io(e,t,n,r){e.setHeader("WWW-Authenticate",Y),e.status(401).json({error:t,code:n,...r?{hint:r}:{}})}function Zd(e){let t=e??"";for(;t.endsWith("/");)t=t.slice(0,-1);return`${t}/ai-access#links`}function el(e,t,n,r){let o=vn(e,Zd(t));return{error:Jd,code:Vd,...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 wn(e,t,n,r,o){t.setHeader("WWW-Authenticate",Y),t.status(401).json(el(e,n,r,o))}function tl(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 Xn(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 rl(e,t){let n=nl;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: ${Xn("get_link")}`:e==="rest"?`This REST endpoint requires a rest token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${Xn("rest")}`:`This MCP endpoint requires an mcp token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${Xn("mcp")}`}function bn(e,t,n){e.setHeader("WWW-Authenticate",Y),e.status(403).json({error:`Access link kind mismatch: expected ${t}, got ${n}`,code:Yd,expectedKind:t,actualKind:n,hint:rl(t,n),tellUser:Xn(t)})}function Nt(e,t,n){return(r,o,s)=>{if(!ge(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=Qd(r.headers.authorization);if(i.kind==="basic"){io(o,Kd,Xd);return}if(i.kind==="missing"||i.kind==="other"){io(o,le,Le);return}let d=t.validateResult(i.token);if(d.status==="expired"){wn(r,o,e.settings.httpAdminRoot,d.expiresAt);return}if(d.status!=="active"){io(o,st,yn,tl(i.token));return}if(d.link.kind!==n){bn(o,n,d.link.kind);return}r.accessLink=d.link,s()}}var st,Jd,le,Kd,Y,Le,yn,Vd,Xd,Yd,nl,it=U(()=>{"use strict";ie();ye();st="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.",Jd="Access link token has expired. Ask the user for a new access link.",le="Access link Bearer token is missing. Ask the user for a new access link and send Authorization: Bearer <token>.",Kd="Authorization must use the Bearer scheme (Authorization: Bearer <access-link-token>), not Basic. Ask the user for a new access link.",Y='Bearer realm="node-red-ai-access", charset="UTF-8"',Le="access_link_missing",yn="access_link_invalid",Vd="token_expired",Xd="bearer_scheme_required",Yd="wrong_token_kind";a(Qd,"parseAuthorizationHeader");a(io,"sendUnauthorized");a(Zd,"accessLinksPagePath");a(el,"tokenExpiredBody");a(wn,"sendTokenExpired");a(tl,"truncatedBearerHint");nl="ask the owner: Node-RED /ai-access \u2192 Access tokens \u2192 create kind=rest/mcp (or get_link)";a(Xn,"whatToTellUserWrongTokenKind");a(rl,"wrongTokenKindHint");a(bn,"sendWrongTokenKind");a(Nt,"requireAccessLink")});var J,z,be,Ce,Qn,ve=U(()=>{"use strict";J=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}},z=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}},be=class extends Error{static{a(this,"AdapterUnavailableError")}code="adapter_unavailable";statusCode=502;constructor(t){super(t),this.name="AdapterUnavailableError"}},Ce=class extends Error{static{a(this,"PluginMisconfiguredError")}code="plugin_misconfigured";statusCode=502;constructor(t){super(t),this.name="PluginMisconfiguredError"}},Qn="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 Ee(e,t){if(!e||!ol.test(e)||e.includes(".."))throw new Error(`Invalid ${t}`);return e}function Zn(e){if(!e||!sl.test(e)||e.includes(".."))throw new Error("Invalid proposal id");return e}function ao(e,t){let n=(0,dt.basename)(t);if(!n||n!==t||n.includes(".."))throw new Error("Invalid file name");let r=(0,dt.resolve)(e),o=(0,dt.resolve)(r,n);if(o!==r&&!o.startsWith(r+dt.sep))throw new Error("Path escapes storage directory");return o}function Rs(e,t=1e5){if(typeof e=="string")return e.replace(/\0/g,"").slice(0,t)}function Ft(e){let t=e&&typeof e=="object"&&!Array.isArray(e)?e:{},n={};for(let o of il){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=Rs(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>j)throw new X(`flow.${o}`,d,j);n[o]=JSON.parse(i)}let r=Buffer.byteLength(JSON.stringify(n),"utf8");if(r>j)throw new X("flow",r,j);return n}function _s(e){let t={};for(let n of al){if(!(n in e))continue;let r=e[n];if(dl.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=Rs(r,2e3);o!==void 0&&(t[n]=o)}return t}var dt,ol,sl,il,al,dl,er=U(()=>{"use strict";dt=require("path");G();ol=/^[A-Za-z0-9_.-]+$/,sl=/^prop-[A-Za-z0-9_-]+$/,il=["id","type","label","disabled","info","env"],al=["proposalId","flowId","expectedRev","resultRev","reason","code","currentRev","title","displayName","flowLabel","linkId","label","expiresAt","createdAt","ttlSeconds","kind"],dl=new Set(["ttlSeconds"]);a(Ee,"assertSafeSegment");a(Zn,"assertSafeProposalId");a(ao,"resolveUnderDir");a(Rs,"sanitizeString");a(Ft,"sanitizeFlowTab");a(_s,"sanitizeAuditDetails")});function lo(e,t){return`oauth-${(0,nr.createHash)("sha256").update(`${e}\0${t}`).digest("hex")}`}function lt(e){return e.kind==="oauth"?lo(e.clientId,e.user):e.linkId}function ll(e){return e==="pending"||e==="approved"||e==="rejected"||e==="failed"||e==="withdrawn"||e==="expired"||e==="superseded"}function Ls(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e.slice(0,cl)){if(!n||typeof n!="object"||Array.isArray(n))continue;let r=n;if(typeof r.code!="string"||!r.code||typeof r.error!="string"||!r.error)continue;let o={code:r.code.replace(/\0/g,"").slice(0,80),error:r.error.replace(/\0/g,"").slice(0,500)};typeof r.nodeId=="string"&&r.nodeId&&(o.nodeId=r.nodeId.replace(/\0/g,"").slice(0,200)),typeof r.type=="string"&&r.type&&(o.type=r.type.replace(/\0/g,"").slice(0,200)),typeof r.property=="string"&&r.property&&(o.property=r.property.replace(/\0/g,"").slice(0,200)),typeof r.configType=="string"&&r.configType&&(o.configType=r.configType.replace(/\0/g,"").slice(0,200)),Array.isArray(r.candidates)&&(o.candidates=r.candidates.filter(s=>typeof s=="string"&&s.length>0).slice(0,20).map(s=>s.replace(/\0/g,"").slice(0,200))),t.push(o)}return t.length>0?t:void 0}var nr,ze,Cs,ne,te,Ot,kn,cl,tr,Ge=U(()=>{"use strict";nr=require("crypto"),ze=require("fs"),Cs=require("path");er();G();a(lo,"oauthOwnerId");a(lt,"proposalOwnerId");ne=class extends Error{static{a(this,"ProposalNotFoundError")}code="proposal_not_found";statusCode=404;constructor(t){super(`Proposal '${t}' was not found`),this.name="ProposalNotFoundError"}},te=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"}},Ot=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"}},kn=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(ll,"isProposalStatus");cl=50;a(Ls,"sanitizeProposalWarnings");tr=class{constructor(t,n={}){this.paths=t;this.now=n.now??(()=>Date.now()),this.randomId=n.randomId??(()=>(0,nr.randomBytes)(12).toString("hex")),this.maxProposalBytes=n.maxProposalBytes??j}static{a(this,"ProposalStore")}inFlight=new Set;now;randomId;maxProposalBytes;fileFor(t){let n;try{n=Zn(t)}catch{throw new ne(t)}return ao(this.paths.proposals,`${n}.json`)}toDiskRecord(t){let n=Zn(t.id),r=Ee(String(t.expectedRev).slice(0,200),"expectedRev"),o=Ee(String(t.payload.flowId).slice(0,200),"flowId"),s=Ft(t.payload.flow),i=t.baseFlow!==void 0?Ft(t.baseFlow):void 0,d=t.createdBy.kind==="oauth"?{kind:"oauth",clientId:Ee(String(t.createdBy.clientId).slice(0,200),"clientId"),user:String(t.createdBy.user).replace(/\0/g,"").slice(0,200)}:{kind:"access-link",linkId:Ee(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:ll(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=Ls(t.warnings);if(c&&(l.warnings=c),t.decidedBy)if(t.decidedBy.kind==="access-link"){let u={kind:"access-link",linkId:Ee(String(t.decidedBy.linkId).slice(0,200),"linkId")};typeof t.decidedBy.label=="string"&&(u.label=t.decidedBy.label.replace(/\0/g,"").slice(0,200)),l.decidedBy=u}else{let u={kind:"admin"};typeof t.decidedBy.username=="string"&&(u.username=t.decidedBy.username.replace(/\0/g,"").slice(0,200)),l.decidedBy=u}return typeof t.reason=="string"&&(l.reason=t.reason.replace(/\0/g,"").slice(0,2e3)),typeof t.resultRev=="string"&&(l.resultRev=Ee(t.resultRev.slice(0,200),"resultRev")),l}writeAtomic(t,n){let r=this.fileFor(t),o=this.toDiskRecord(n),s=Be(o,this.maxProposalBytes,{kind:"proposal",pretty:!0});Rt(this.paths.root,r,s)}create(t){let n=new Date(this.now()).toISOString(),r=Zn(`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=Ls(t.warnings);return s&&(o.warnings=s),this.writeAtomic(r,o),this.get(r)}get(t){let n=this.fileFor(t);if(!(0,ze.existsSync)(n))throw new ne(t);return JSON.parse((0,ze.readFileSync)(n,"utf8"))}list(t){if(!(0,ze.existsSync)(this.paths.proposals))return[];let n=(0,ze.readdirSync)(this.paths.proposals).filter(o=>o.endsWith(".json")),r=[];for(let o of n){let s=(0,Cs.basename)(o);if(!(s!==o||s.includes("..")))try{let i=JSON.parse((0,ze.readFileSync)(ao(this.paths.proposals,s),"utf8"));if(t?.linkId&&lt(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 kn(t);let n=this.get(t);if(n.status!=="pending")throw new te(t,n.status);return this.inFlight.add(t),n}releaseLock(t){this.inFlight.delete(t)}markApproved(t,n,r){let o=this.get(t);return o.status="approved",o.decidedBy=n,o.resultRev=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markRejected(t,n,r){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){let o=this.get(t);if(o.status!=="pending")throw new te(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(s.status!=="pending")throw new te(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)}}});function Es(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 rr(e,t,n){let r=e.find(d=>d.id===t);if(!r||r.type!=="tab"&&r.type!=="subflow")throw new z(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({...Es(l,`flowData.nodes[${d}]`),z:t});for(let[d,l]of(n.configs??[]).entries())s.push({...Es(l,`flowData.configs[${d}]`),z:t});return[...e.filter(d=>d.id!==t&&d.z!==t),o,...s]}function An(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 $t(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 z(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 z(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 or=U(()=>{"use strict";ve();a(Es,"asFlowNode");a(rr,"replaceFlowTabInFlatFlows");a(An,"assertFlowsSnapshot");a($t,"normalizeFlowTab")});function fo(e){return e?ul.has(e)?!0:e.startsWith("subflow:"):!1}function Je(e,t="node.type"){if(e==null||e==="")throw new y(`${t} is required`,"missing_node_type");if(typeof e!="string")throw new y(`${t} must be a non-empty string (got ${typeof e})`,"invalid_node_type",{details:{type:e,got:typeof e}});if(!e.trim())throw new y(`${t} is required`,"missing_node_type");return e}function wl(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 bl(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 kl(e){return Math.max(2,Math.floor(e.length/3))}function mo(e,t,n=5){if(typeof e!="string"||!e)return[];let r=kl(e);return t.filter(s=>typeof s=="string"&&s!==e&&!vl.has(s)&&!s.startsWith("_")&&!s.startsWith("subflow:")).map(s=>({c:s,dist:wl(e,s),prefix:bl(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 Al(e){return e.endsWith("/")?e.slice(0,-1):e}function Ds(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Ms.createRequire)(e)}function xl(){try{let t=Ds()("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}function js(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 ho(e=yl,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 Hs(e={}){if(xl())try{let n=Ds()("@node-red/runtime");if(n.nodes&&typeof n.nodes.getNodeList=="function")return new po(n.nodes)}catch{}if(e.baseUrl)return new uo({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 go(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 Sl(e,t){let n=t.toLowerCase();return[e.type,e.module,e.category??"",e.description??""].join(`
4
- `).toLowerCase().includes(n)}async function ir(e,t={}){let n=t.limit??pl,{byType:r}=await go(e),o=[...r.values()].sort((u,p)=>u.type.localeCompare(p.type)),s=t.q?.trim();s&&(o=o.filter(u=>Sl(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 Tl(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 Ns(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 Pl(e,t=2e3){let n=Ns(e,"script");return n=Ns(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 Rl(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:Fs(e,d,i)}return Fs(e,n.index,r)}catch{return null}}function Fs(e,t,n){let r=e.slice(t),o=r.indexOf("{");if(o<0)return{type:n};let s=xn(r.slice(o));if(!s)return{type:n};let i=Os(s,"category"),d=Os(s,"color"),l=$s(s,"inputs"),c=$s(s,"outputs"),u=Ll(s),p=[];if(u){for(let g of Object.values(u))if(g&&typeof g=="object"&&!Array.isArray(g)){let x=g.type;typeof x=="string"&&x.length>0&&p.push(x)}}let f,m=Tl(e,n);m&&(f=Pl(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 _l(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 xn(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 Os(e,t){let n=new RegExp(`\\b${t}\\s*:\\s*['"]([^'"]+)['"]`);return e.match(n)?.[1]}function $s(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 Ll(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=xn(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=xn(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=Il(m)}s[l]=u}return Object.keys(s).length>0?s:void 0}function Cl(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 El(e){return e==="_DEFAULT_"?"":e}function sr(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=Cl(t,o+1);s+=d.char,o=d.next;continue}if(i===r)break;s+=i,o+=1}return El(s)}if(t.startsWith("[")){let r=Us(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=sr(d);s.push(l);let c=co(d);i+=c>0?c:1}return s}if(t.startsWith("{")){let r=xn(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=sr(o.slice(i));d=typeof c=="string"?c:"",i+=co(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]=sr(l),i+=co(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 co(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=Us(t);return n+(o?.length??1)}if(t.startsWith("{")){let o=xn(t);return n+(o?.length??1)}let r=/^(?:-?\d+(?:\.\d+)?|true|false|null)/.exec(t);return r?n+r[0].length:n+1}function Us(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 Il(e){return sr(e)}function Nl(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 yo(e,t){let n=e.toLowerCase();return t.includes(e)?e:t.find(r=>r.toLowerCase()===n)}async function ct(e,t,n={}){if(!t||typeof t!="string")throw new y("Missing node type","missing_node_type");let r=n.includeHelp!==!1;if(fo(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=Nl(o,t);if(!i){let l=yo(t,s);throw l&&l!==t?new y(`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 y(`Node type '${t}' was not found`,"node_type_not_found",{statusCode:404,details:{type:t,nearest:mo(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=Rl(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=_l(c.help)))}}catch{}return d}async function Bs(e,t){let{allNames:n}=await go(e),r=new Set(n),o=new Set,s=a(l=>{let c=Je(l,"node.type");if(o.has(c)||fo(c)){o.add(c);return}if(o.add(c),r.has(c))return;let u=yo(c,n);throw u&&u!==c?new y(`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 y(`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:mo(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 qs(e,t){let n=Je(t,"node.type");if(fo(n))return;let{allNames:r}=await go(e);if(r.includes(n))return;let o=yo(n,r);throw o&&o!==n?new y(`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 y(`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:mo(n,r,5)}})}async function Fl(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 Ol(e,t){let n=Date.now();if(ke&&n-ke.fetchedAt<gl)return ke.body;let r=ke?.etag,o=await e(t,r);if(o.status===304&&ke)return ke={...ke,fetchedAt:n,...o.etag?{etag:o.etag}:{}},ke.body;if(!o.body||!Array.isArray(o.body.modules)){if(ke)return ke.body;throw new y("Node-RED catalogue unavailable","catalogue_unavailable",{statusCode:502})}return ke={fetchedAt:n,body:o.body,...o.etag?{etag:o.etag}:{}},o.body}async function ar(e,t,n={}){let r=t.trim();if(!r)throw new y("q query parameter is required","missing_query");let o=n.limit??fl,s=n.catalogueUrl??hl,i=n.catalogueFetcher??Fl,d=await Ol(i,s),l=Array.isArray(d.modules)?d.modules:[],c=await e.listNodeSets(),u=new Set(c.flatMap(g=>{let x=[g.module,g.id];return g.id.includes("/")&&x.push(g.id.split("/")[0]??g.module),x.map(S=>S.toLowerCase())})),p=r.toLowerCase(),f=[];for(let g of l){let x=g.id.toLowerCase(),S=(g.keywords??[]).map(ot=>String(ot).toLowerCase()),E=(g.description??"").toLowerCase(),I=$l(p,x,S,E);if(I<=0)continue;let N={module:g.id,installed:u.has(x)};typeof g.description=="string"&&(N.description=g.description),typeof g.version=="string"&&(N.version=g.version),typeof g.updated_at=="string"&&(N.updated=g.updated_at),typeof g.downloads=="number"&&(N.downloads=g.downloads),typeof g.url=="string"&&(N.link=g.url),f.push({item:N,score:I})}f.sort((g,x)=>x.score-g.score||(x.item.downloads??0)-(g.item.downloads??0)||g.item.module.localeCompare(x.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:ml}}function $l(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 Ms,ul,pl,fl,ml,hl,gl,yl,vl,uo,Is,po,ke,ut=U(()=>{"use strict";Ms=require("module");K();ul=new Set(["tab","subflow","group","junction"]),pl=100,fl=30,ml="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.",hl="https://catalogue.nodered.org/catalogue.json",gl=3600*1e3,yl=["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(fo,"isStructuralNodeType");a(Je,"requireNodeTypeName");a(wl,"editDistance");a(bl,"sharedPrefixLength");vl=new Set(["unknown","tab","subflow","group"]);a(kl,"nearestDistanceCutoff");a(mo,"nearestNodeTypes");a(Al,"trimTrailingSlash");a(Ds,"createPluginRequire");a(xl,"isRuntimeSettingsAvailable");a(js,"normalizeNodeSet");a(ho,"createStaticNodeTypesAdapter");uo=class{static{a(this,"HttpNodeTypesAdapter")}baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=Al(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=js(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()}},Is={user:null,req:null},po=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(Is))}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=js(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",...Is}));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(Hs,"createNodeTypesAdapter");a(go,"flattenInstalledTypes");a(Sl,"matchesQuery");a(ir,"listNodeTypes");a(Tl,"extractHelpScriptBody");a(Ns,"removeHtmlBlocks");a(Pl,"stripHelpText");a(Rl,"parseRegisterTypeFromHtml");a(Fs,"parseRegisterTypeAt");a(_l,"splitHelpSections");a(xn,"extractBalancedObject");a(Os,"matchStringProp");a($s,"matchNumberProp");a(Ll,"parseDefaultsBlock");a(Cl,"unescapeJsStringEscape");a(El,"normalizeDefaultValue");a(sr,"parseJsLiteral");a(co,"measureJsLiteralLength");a(Us,"extractBalancedArray");a(Il,"parseLooseValue");a(Nl,"findSetForType");a(yo,"findCaseInsensitiveType");a(ct,"getNodeTypeDetail");a(Bs,"assertKnownNodeTypes");a(qs,"assertKnownNodeType");ke=null;a(Fl,"defaultCatalogueFetcher");a(Ol,"loadCatalogue");a(ar,"searchPalette");a($l,"paletteMatchScore")});function Ke(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 y(`${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 y(t.error,t.code,{statusCode:o,details:r})}function wo(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 Ml(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 zs(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 ${Ml(c)}; node ids must be unique across all flows`,nodeId:l})}return o}function Ws(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 jl(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]);for(let r of n)if(!Dl.has(r)&&JSON.stringify(e[r])!==JSON.stringify(t[r]))return!0;return!1}function Hl(e,t){let n=Ws(t);if(!e)return new Set(n.keys());let r=Ws(e),o=new Set;for(let[s,i]of n){let d=r.get(s);(!d||jl(d,i))&&o.add(s)}return o}function Gs(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(...wo(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{Je(c.type,"node.type")}catch(u){u instanceof y&&n.push({code:u.code,error:u.message,...typeof c.id=="string"?{nodeId:c.id}:{}})}"wires"in c&&Mt(c.wires,n,typeof c.id=="string"?c.id:void 0)}return n}function Mt(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 Ul(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 ct(t,n,{includeHelp:!1})).outputs}catch{return}}function Bl(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 ql(e){return Array.isArray(e)&&e.some(t=>typeof t=="string"&&t.length>0)}function Wl(e,t){return t<=0?!1:!("wires"in e)||e.wires===void 0?!0:Array.isArray(e.wires)?!e.wires.some(ql):!1}async function bo(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 Ul(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(!Mt(d.wires,r,d.id))continue;let p=d.wires;if(l!==void 0){let f=Bl(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&&Wl(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 vo(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 zl(e,t){return e.filter(n=>n.type===t).map(n=>n.id)}async function ko(e,t,n){let r=[],o=[...t];for(let s of e){let i;try{i=Je(s.type,"node.type")}catch{continue}let d;try{d=await ct(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],x=zl(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:x});continue}o.find(E=>E.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:x})}}return r}async function Js(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(...vo(r,n));let d=t.baseFlow?Hl(t.baseFlow,e):void 0,l=await bo(r,t.nodeTypes,{...d?{softWarningNodeIds:d}:{}});if(s.push(...l.errors),i.push(...l.warnings),t.nodeTypes){let c=await ko(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 Dl,Ao=U(()=>{"use strict";K();ut();a(Ke,"throwValidationIssues");a(wo,"collectDuplicateNodeIdIssues");a(Ml,"describeLiveNode");a(zs,"collectCrossFlowDuplicateIssues");Dl=new Set(["x","y","w","h","g","id","z"]);a(Ws,"collectNodesMap");a(jl,"nodeContentChanged");a(Hl,"changedNodeIds");a(Gs,"collectFullFlowShapeIssues");a(Mt,"assertValidWiresShape");a(Ul,"resolveNodeOutputCount");a(Bl,"wireCountIssue");a(ql,"portHasTargets");a(Wl,"nodeIsUnwired");a(bo,"collectWireValidation");a(vo,"collectZMismatchIssues");a(zl,"configCandidates");a(ko,"collectConfigWarnings");a(Js,"validateFlowTab")});function Ve(e,t="node"){return typeof e=="string"&&e.trim()?e.trim():t}function dr(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 Vs(e,t){e[Ks]=t}function Kl(e){if(!e)return;let t=e[Ks];return t instanceof Map?t:void 0}function Vl(e){return typeof e!="string"||!e.trim()?new Set:new Set(e.split(",").map(t=>t.trim()).filter(Boolean))}function Xl(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 re(e){let t=Vl(e.include);return Xl(e.include_payload)&&(t.add("payload"),t.add("baseFlow")),t}function q(e,t){let n={req:e};t!==void 0&&(n.httpAdminRoot=t);let r=Kl(e);return r&&(n.liveFlowLabels=r),n}function Dt(e){return typeof e.name=="string"&&e.name.trim()?e.name.trim():typeof e.label=="string"&&e.label.trim()?e.label.trim():""}function Sn(e){return`"${e.replace(/"/g,"'")}"`}function jt(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 Yl(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 Ql(e,t){let n=Dt(e),r=Dt(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 xo(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&&Yl(d,i)&&o.push({before:d,after:i})}return{added:n,removed:r,modified:o}}function lr(e){return e===1?"node":"nodes"}function Zl(e){return e.length===1&&e[0]?`adds 1 ${Ve(e[0].type)}`:`adds ${e.length} ${lr(e.length)}`}function ec(e){return`deletes ${e} ${lr(e)}`}function tc(e){return`edits ${e} ${lr(e)}`}function nc(e){let t=[];e.removed>0&&t.push(ec(e.removed));let n=e.added??[],r=e.addedCount??n.length;r>0&&t.push(n.length===r?Zl(n):`adds ${r} ${lr(r)}`),(e.modified??0)>0&&t.push(tc(e.modified??0));let o=t.length?t.join(", "):"replaces nodes",s=e.flowLabel?.trim();return s?`Replace ${s}: ${o}`:`Replace flow: ${o}`}function rc(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=Ve(u.type),f=Dt(u);return f?`Add ${p} node ${Sn(f)}`:`Add ${p} node`}if(n[0]){let u=n[0],p=Ve(u.type),f=Dt(u);return f?`Remove ${p} node ${Sn(f)}`:`Remove ${p} node`}let s=r[0];if(!s)return;let i=Ve(s.after.type,Ve(s.before.type)),d=Dt(s.before),l=Dt(s.after);if(Ql(s.before,s.after)&&d&&l)return`Rename ${i} node ${Sn(d)} \u2192 ${Sn(l)}`;let c=l||d;return c?`Edit ${i} ${Sn(c)}`:`Edit ${i}`}function So(e,t){return typeof e?.label=="string"&&e.label.trim()?e.label.trim():typeof t?.label=="string"&&t.label.trim()?t.label.trim():""}function cr(e,t){if(typeof e.flowLabel=="string"&&e.flowLabel.trim())return e.flowLabel.trim();let n=So(e.payload?.flow,e.baseFlow);if(n)return n;let r=e.payload?.flowId;return r&&t?.has(r)?t.get(r)??"":""}function Xs(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 To(e,t){let n=xo(jt(e??{}),jt(t)),r=n.added.length+n.removed.length+n.modified.length,o=So(t,e);if(r===0)return o?`Update ${o}`:"Update flow";if(n.removed.length>0)return nc({...o?{flowLabel:o}:{},removed:n.removed.length,added:n.added,modified:n.modified.length});if(r>Jl)return o?`${r} changes in ${o}`:`${r} changes`;let s=rc(n)??(o?`Update ${o}`:"Update flow");return Xs(s,o)}function oc(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 Po(e,t){return`${e} \xB7 ${ic(t)}`}function sc(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 ic(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=sc(-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,Gl).trim();return t.length>0?t:void 0}function ac(e,t){let n=ae(t);return`${n==="/"?"/ai-access":`${n}/ai-access`}#proposal=${encodeURIComponent(e)}`}function Ro(e,t={}){let n=cr(e,t.liveFlowLabels),r=ur(e.title),o=To(e.baseFlow,n&&!So(e.payload.flow,e.baseFlow)?{...e.payload.flow,label:n}:e.payload.flow),s=xo(jt(e.baseFlow??{}),jt(e.payload.flow)),i=oc(r,s.removed.length)?o:r??o;return Xs(i,n)}function dc(e){return typeof e.storedTitle=="string"&&e.storedTitle?e.storedTitle:ur(e.title)}function oe(e,t={}){let n=Ro(e,t),r=cr(e,t.liveFlowLabels),o=dc(e)??n,s=ac(e.id,t.httpAdminRoot),i=t.req?vn(t.req,s):s;return{...e,title:n,storedTitle:o,flowLabel:r,displayName:Po(n,e.createdAt),reviewUrl:i,reviewPath:s}}function lc(e){let t=xo(jt(e.baseFlow??{}),jt(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 cc(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 ce(e,t={}){let n=oe(e,t),r=t.include??new Set,o={id:n.id,status:n.status,type:n.type,title:n.title,storedTitle:n.storedTitle,displayName:n.displayName,flowLabel:n.flowLabel,reviewUrl:n.reviewUrl,reviewPath:n.reviewPath,createdAt:n.createdAt,updatedAt:n.updatedAt,expectedRev:n.expectedRev,createdBy:cc(n.createdBy),changeSummary:lc(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),n.baseRevStale!==void 0&&(o.baseRevStale=n.baseRevStale),n.decidedBy!==void 0&&(o.decidedBy=n.decidedBy),n.reason!==void 0&&(o.reason=n.reason),n.resultRev!==void 0&&(o.resultRev=n.resultRev),o}function Ys(e,t={}){return e.map(n=>ce(n,t))}var Gl,Jl,Ks,Ae=U(()=>{"use strict";ie();_e();Gl=80,Jl=1;a(Ve,"nodeTypeText");Ks=Symbol.for("ai-access.liveFlowLabels");a(dr,"liveFlowLabelsFromFlows");a(Vs,"setRequestFlowLabels");a(Kl,"getRequestFlowLabels");a(Vl,"parseIncludeQuery");a(Xl,"isTruthyFlag");a(re,"resolveIncludeFromQuery");a(q,"presentOptionsFor");a(Dt,"nodeLabel");a(Sn,"quoteName");a(jt,"collectNodes");a(Yl,"nodeContentChanged");a(Ql,"isRenameOnly");a(xo,"classifyFlowChanges");a(lr,"pluralNodes");a(Zl,"describeAddedClause");a(ec,"describeRemovedClause");a(tc,"describeModifiedClause");a(nc,"buildReplaceFlowTitle");a(rc,"describeOneChange");a(So,"flowLabelHint");a(cr,"resolveFlowLabel");a(Xs,"ensureTitleIncludesFlowName");a(To,"autoGenerateProposalTitle");a(oc,"shouldRefreshDisplayTitle");a(Po,"buildProposalDisplayName");a(sc,"formatUtcOffset");a(ic,"formatProposalSubmittedAt");a(ur,"sanitizeProposalTitle");a(ac,"buildProposalReviewPath");a(Ro,"resolveProposalTitle");a(dc,"storedTitleOf");a(oe,"presentProposal");a(lc,"buildChangeSummary");a(cc,"presentCreatedByForApi");a(ce,"presentProposalForApi");a(Ys,"presentProposalsForApi")});function Qs(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 pc(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let o of n)uc.has(o)||JSON.stringify(e[o])!==JSON.stringify(t[o])&&r.push(o);return r.sort()}function Zs(e){let t=Qs(e.baseFlow??{}),n=Qs(e.payload.flow),r=new Map;for(let[o,s]of n){let i=t.get(o);if(!i){r.set(o,new Set(["*"]));continue}let d=pc(i,s);d.length>0&&r.set(o,new Set(d))}for(let[o]of t)n.has(o)||r.set(o,new Set(["*"]));return r}function fc(e,t){let n=[],r=[];for(let[o,s]of e){let i=t.get(o);if(!i)continue;n.push(o);let d=[];for(let c of s){if(c==="*"){d.push(...[...i].filter(u=>u!=="*")),i.has("*")&&d.push("*");continue}(i.has(c)||i.has("*"))&&d.push(c)}let l=[...new Set(d)].sort();l.length>0&&r.push({nodeId:o,fields:l})}return n.sort(),{nodeIds:n,fields:r}}function V(e,t,n={}){let r=0;for(let o of e.list())if(o.status==="pending"&&!(n.flowId&&o.payload.flowId!==n.flowId)&&o.expectedRev!==t)try{e.markClosed(o.id,"expired",mc),r+=1}catch{}return r}function ei(e,t,n,r){let o=`Superseded by approved proposal ${n} (resultRev ${r}). Re-read the flow and resubmit if still needed.`,s=0;for(let i of e.list())if(!(i.id===n||i.status!=="pending"||i.payload.flowId!==t))try{e.markClosed(i.id,"superseded",o),s+=1}catch{}return s}function Ie(e,t,n){let r=Zs(t),o=[];for(let s of e.list()){if(s.id===t.id||s.status!=="pending"||s.payload.flowId!==t.payload.flowId)continue;let i=Ro(s),d=n?lt(s.createdBy)===n:lt(s.createdBy)===lt(t.createdBy),l=fc(r,Zs(s)),c=s.expectedRev===t.expectedRev,u=l.nodeIds.length>0;if(!c&&!u)continue;let p={id:s.id,title:i,displayName:Po(i,s.createdAt),mine:d};l.nodeIds.length>0&&(p.overlappingNodeIds=l.nodeIds),l.fields.length>0&&(p.overlappingFields=l.fields),o.push(p)}return{conflictingProposals:o}}function Ne(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 Q(e){return e>0?{autoClosedCount:e}:{}}var uc,mc,Xe=U(()=>{"use strict";Ge();Ae();uc=new Set(["x","y","w","h","g","id","z"]);a(Qs,"collectNodes");a(pc,"changedFieldKeys");a(Zs,"computeProposalTouches");a(fc,"overlapBetween");mc="Proposal expired: the live flow revision moved past expectedRev. Re-read the flow and resubmit.";a(V,"expireStalePendingProposals");a(ei,"supersedePendingOnFlow");a(Ie,"findConflictingProposals");a(Ne,"conflictWarningFields");a(Q,"autoClosedFields")});var di={};ds(di,{applyFlowNodeOps:()=>pt,applyReplaceTextToValue:()=>ai,buildPatchedFlowTab:()=>Tn,collectSetProps:()=>_o,decodeBase64UrlJson:()=>Pn,findTextOccurrences:()=>ii,interpretPatch:()=>Rn});function si(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function hc(e){if(typeof e!="object"||e===null||Array.isArray(e))throw new y(`add op needs node as an object (got ${si(e)})`,"invalid_node")}async function Tn(e,t,n){return pt(e,t,n.map(r=>({op:"set",nodeId:r.nodeId,set:r.set})))}function ii(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 ai(e,t,n,r,o){if(oi.has(t))throw new y(`Cannot change protected node property '${t}' via replace_text`,"protected_property");if(typeof n!="string"||n.length===0)throw new y("find must be a non-empty string","missing_find");if(typeof r!="string")throw new y("replace must be a string","invalid_replace");if(e===void 0)throw new y(`Field '${t}' was not found on the node`,"field_not_found");if(typeof e!="string")throw new y(`Field '${t}' is not a string (got ${e===null?"null":typeof e})`,"field_not_string");let s=ii(e,n);if(s.length===0)throw new y(`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 y(`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 y(`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 y(`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 gc(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 ${si(r)})`});continue}let o=r;(!o.id||typeof o.id!="string")&&t.push({code:"missing_node_id",error:"add op needs node.id"});try{Je(o.type,"node.type")}catch(s){s instanceof y&&t.push({code:s.code,error:s.message,nodeId:o.id})}"wires"in o&&Mt(o.wires,t,typeof o.id=="string"?o.id:void 0)}else n.op==="set"&&typeof n.set=="object"&&"wires"in n.set&&Mt(n.set.wires,t,n.nodeId);return t}function yc(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 pt(e,t,n,r={}){if(!t)throw new y("flow id is required","missing_flow_id");if(!n.length)throw new y("At least one node edit is required","missing_edits");let o=gc(n);o.push(...wo([n.flatMap(h=>h.op==="add"?[h.node]:[])],"the batch (add ops)")),Ke(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 qs(r.nodeTypes,h.node.type),ni(c,t,h)}catch(g){if(g instanceof y)u.push({code:g.code,error:g.message,...g.statusCode!==400?{statusCode:g.statusCode}:{},...g.details??{}});else throw g}u.push(...vo(c,t));let p=yc(n),f=await bo(c,r.nodeTypes,{softWarningNodeIds:p});u.push(...f.errors),Ke(u);for(let h of n)ni(i,t,h);let m=[...f.warnings];if(r.nodeTypes){let h=await ko(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 ti(e,t,n){if(!t||typeof t!="string")throw new y("Each edit needs a node id","missing_node_id");let r=e.findIndex(o=>o.id===t);if(r<0||!e[r])throw new y(`Node '${t}' was not found in flow '${n}'`,"node_not_found",{statusCode:404});return r}function ni(e,t,n){if(n.op==="add"){let i=n.node;if(!i.id||typeof i.id!="string")throw new y("add op needs node.id","missing_node_id");if(Je(i.type,"node.type"),e.some(d=>d.id===i.id))throw new y(`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=ti(e,n.nodeId,t);e.splice(i,1);return}let r=ti(e,n.nodeId,t),o=e[r];if(!o)throw new y(`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 y(`Field '${n.field}' was not found on the node`,"field_not_found");let i=ai(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(oi.has(i))throw new y(`Cannot change protected node property '${i}' via GET propose`,"protected_property");if(i==="wires"){let l=[];Mt(d,l,n.nodeId),Ke(l)}s[i]=d}e[r]=s}function ri(e){if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean"||typeof e=="bigint")return String(e)}function _o(e){let t={},n=e.set;if(n&&typeof n=="object"&&!Array.isArray(n))for(let[r,o]of Object.entries(n)){let s=ri(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=ri(o);i!==void 0&&(t[s]=i)}}return t}function Pn(e){let t;try{t=Buffer.from(e,"base64url").toString("utf8")}catch{throw new y("patch must be base64url-encoded JSON","invalid_patch")}try{return JSON.parse(t)}catch{throw new y("patch JSON is malformed","invalid_patch_json")}}function wc(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 Lo(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 bc(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 y("occurrence must be a positive integer","invalid_occurrence");if(!Number.isFinite(t))throw new y("occurrence must be a positive integer","invalid_occurrence");return Math.trunc(t)}function vc(e){let t=e.node;if(t!==void 0)return hc(t),{op:"add",node:t};let n={...e};return delete n.op,{op:"add",node:n}}function kc(e){let t=Lo(e);if(!t)throw new y("replace_text op needs node id","missing_node_id");let n=typeof e.field=="string"?e.field:"";if(!n)throw new y("replace_text op needs field","missing_field");if(typeof e.find!="string")throw new y("replace_text op needs find string","missing_find");if(typeof e.replace!="string")throw new y("replace_text op needs replace string","invalid_replace");let r=bc(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 Ac(e){let t=Lo(e);if(!t)throw new y("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 xc(e){let t=typeof e.op=="string"?e.op.toLowerCase():"set";if(t==="add")return vc(e);if(t==="remove"){let n=Lo(e);if(!n)throw new y("remove op needs id","missing_node_id");return{op:"remove",nodeId:n}}if(t==="replace_text")return kc(e);if(t!=="set")throw new y("nodes[].op must be 'add', 'remove', 'set', or 'replace_text'","invalid_patch");return Ac(e)}function Rn(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new y("patch must be a JSON object","invalid_patch");let n=e;if(n.type==="replace_flow"||n.flow&&typeof n.flow=="object"){let r=wc(n,t);if(!r)throw new y("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 y("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 y("Each patch.nodes entry must be an object","invalid_patch");o.push(xc(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 y("patch must include nodes[] edits or type/flow for replace_flow","invalid_patch")}var oi,pr=U(()=>{"use strict";K();Ao();ut();oi=new Set(["id","type","z"]);a(si,"describeNonObjectNode");a(hc,"assertAddNodeObject");a(Tn,"buildPatchedFlowTab");a(ii,"findTextOccurrences");a(ai,"applyReplaceTextToValue");a(gc,"collectOpShapeIssues");a(yc,"touchedNodeIdsFromOps");a(pt,"applyFlowNodeOps");a(ti,"requireNodeIndex");a(ni,"applyOneNodeOp");a(ri,"stringifyQueryValue");a(_o,"collectSetProps");a(Pn,"decodeBase64UrlJson");a(wc,"resolveReplaceFlowId");a(Lo,"resolveNodeId");a(bc,"parseOccurrence");a(vc,"parseAddOp");a(kc,"parseReplaceTextOp");a(Ac,"parseSetOp");a(xc,"parseNodeOp");a(Rn,"interpretPatch")});function Ht(e,t){return lt(e.createdBy)===t.id}function Co(e,t){return!t||t.length===0||t.includes(e)}function Sc(e){return typeof e=="string"&&ci.has(e)}function Bt(e,t){let n=Tc[e],r=e!=="status"&&Array.isArray(t);return new y(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 fr(e,t){if(Array.isArray(t))throw Bt(e,t);return t}function Pc(e){return typeof e=="number"?Number.isInteger(e)?e:null:typeof e=="string"&&/^-?\d+$/.test(e.trim())?Number(e.trim()):null}function Eo(e){return e==null||e===""}function Rc(e){if(!Eo(e)){if(e==="true"||e==="1"||e===!0)return!0;if(e==="false"||e==="0"||e===!1)return!1;throw Bt("mine",e)}}function Io(e){if(Eo(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(!Sc(o))throw Bt("status",e);n.includes(o)||n.push(o)}}return n.length>0?n:void 0}function li(e,t,n,r){if(Eo(t))return;let o=Pc(t);if(o===null||o<n||o>r)throw Bt(e,t);return o}function mr(e){let t={},n=Rc(fr("mine",e.mine));n!==void 0&&(t.mine=n);let r=Io(e.status);r&&(t.status=r);let o=fr("flowId",e.flowId??e.flow_id??e.flow);typeof o=="string"&&o.length>0&&(t.flowId=o);let s=li("limit",fr("limit",e.limit),1,Ut);s!==void 0&&(t.limit=s);let i=li("offset",fr("offset",e.offset),0,Number.MAX_SAFE_INTEGER);return i!==void 0&&(t.offset=i),t}function ft(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 _c(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),Ut);return{items:e.slice(r,r+o),total:n,limit:o,offset:r}}function qt(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||!Ht(c,t)).filter(c=>Co(c.status,r.status)).map(c=>({...oe(c,n),mine:Ht(c,t)})),l=_c(d,r);return{proposals:l.items,total:l.total,limit:l.limit,offset:l.offset}}function De(e,t,n,r={}){if(!n)throw new y("Missing proposal id","missing_proposal_id");let o=e.get(n);return{...oe(o,r),mine:Ht(o,t)}}function Lc(e){let t={kind:"access-link",linkId:e.id};return typeof e.label=="string"&&e.label&&(t.label=e.label),t}function Wt(e,t,n,r,o,s={}){if(!r)throw new y("Missing proposal id","missing_proposal_id");let i;try{i=e.get(r)}catch(f){throw f instanceof ne,f}if(!Ht(i,n))throw new Ot(r);if(i.status!=="pending")throw new te(r,i.status);let d=typeof o=="string"&&o.trim()?o.replace(/\0/g,"").trim().slice(0,2e3):"Withdrawn by access link",l=Lc(n),c=e.markWithdrawn(r,l,d),u=oe(c,s),p=u.flowLabel||void 0;return t.append("proposal.withdrawn",l,{proposalId:c.id,flowId:c.payload.flowId,title:u.title,displayName:u.displayName,reason:d,...p?{flowLabel:p}:{}}),{...u,mine:!0}}function Cc(e,t,n,r={}){let o;try{o=e.get(t.id)}catch{return{}}if(r.liveRev){V(e,r.liveRev,{flowId:o.payload.flowId});try{o=e.get(t.id)}catch{return{}}}return o.status!=="pending"?{}:Ne(Ie(e,o,n))}function ue(e,t,n,r={},o={}){let s="mine"in n?n:{...oe(n,r),mine:Ht(n,t)},i=0;if(o.liveRev)try{let c=e.get(s.id).payload.flowId;i=V(e,o.liveRev,{flowId:c});let u=e.get(s.id);s={...oe(u,r),mine:Ht(u,t)}}catch{}let d=ce(s,r),l={...d,mine:s.mine,...Q(i)};return o.includeConflicts!==!1&&d.status==="pending"&&Object.assign(l,Cc(e,d,t.id)),l}async function pe(e){let t=await e.getFlows();return{rev:t.rev,flows:t.flows}}async function xe(e,t){if(!t)throw new y("Missing flow id","missing_flow_id");let n=await e.getFlow(t);return{rev:n.rev,flow:$t(t,n.flow)}}function Ec(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 ui(e,t){if("nodes"in e&&e.nodes!==void 0&&!Array.isArray(e.nodes))throw new y("payload.nodes must be an array of node ops","invalid_nodes");if(t!==void 0&&!Array.isArray(t))throw new y("nodes must be an array of node ops","invalid_nodes")}async function Ic(e,t,n,r,o){let{applyFlowNodeOps:s,interpretPatch:i}=await Promise.resolve().then(()=>(pr(),di));ui(n,r);let d=Array.isArray(n.nodes)?n.nodes:Array.isArray(r)?r:null;if(!d)throw new y("payload.flow is required","missing_flow");let l=n.flowId,c=typeof l=="string"?l:"";if(!c)throw new y("payload.flowId is required","missing_flow_id");let u=i({nodes:d,flowId:c},c);if(u.kind!=="nodes")throw new y("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 J(t,p.expectedRev,"submit_proposal");return{flowId:u.flowId,flow:p.flow}}async function Nc(e,t,n,r,o){let s=n.flowId,i=n.flow,d=!i||typeof i!="object"||Array.isArray(i);if(d&&ui(n,r),d&&(Array.isArray(n.nodes)||Array.isArray(r))){let l=await Ic(e,t,n,r,o);s=l.flowId,i=l.flow}if(typeof s!="string"||s.length===0)throw new y("payload.flowId is required","missing_flow_id");if(!i||typeof i!="object"||Array.isArray(i))throw new y("payload.flow is required","missing_flow");return{flowId:s,flow:i}}function Fc(e,t){try{return{safeExpectedRev:Ee(e.slice(0,200),"expectedRev"),safeFlowId:Ee(t.slice(0,200),"flowId")}}catch{throw new y("expectedRev and payload.flowId must be safe path segments","invalid_path_segment")}}async function Ye(e,t,n,r,o,s={},i){let d=r.type,l=Ec(r),c=r.payload;if(d!=="replace_flow")throw new y("type must be 'replace_flow'","invalid_proposal_type");if(!l)throw new y("expectedRev is required (top-level field next to type and payload)","missing_expected_rev");if(!c||typeof c!="object"||Array.isArray(c))throw new y("payload must be an object","invalid_payload");let p=await Nc(o,l,c,r.nodes,i),{safeExpectedRev:f,safeFlowId:m}=Fc(l,p.flowId),h=p.flow,g=Buffer.byteLength(JSON.stringify(c),"utf8");if(g>j)throw new X("proposal payload",g,j);Ke(Gs(h,m));let S=Ft(h),E=await o.getFlows();if(E.rev!==f)throw new J(f,E.rev,"submit_proposal");Ke(zs(S,m,E.flows));let I=await o.getFlow(m);if(I.rev!==f)throw new J(f,I.rev,"submit_proposal");let N=Ft(I.flow);i&&await Bs(i,S);let{errors:ot,warnings:me}=await Js(S,{flowId:m,baseFlow:N,...i?{nodeTypes:i}:{}});Ke(ot);let A={kind:"access-link",linkId:n.id};n.label!==void 0&&(A.label=n.label);let v=n.oauthClientId&&n.oauthUser?{kind:"oauth",clientId:n.oauthClientId,user:n.oauthUser}:A,w=ur(r.title)??To(N,S),b=e.create({type:"replace_flow",expectedRev:f,payload:{flowId:m,flow:S},baseFlow:N,title:w,createdBy:v,...me.length>0?{warnings:me}:{}}),k=oe(b,s),L=k.flowLabel||void 0;return t.append("proposal.created",A,{proposalId:b.id,flowId:m,expectedRev:f,title:k.title,displayName:k.displayName,...L?{flowLabel:L}:{},...me.length>0?{warningCount:me.length}:{}}),k}function O(e,t="rest"){return _n(e,t)}var Ut,ci,Tc,y,K=U(()=>{"use strict";ve();or();Ge();er();G();Fe();Ao();ut();Xe();Ae();a(Ht,"isMineProposal");Ut=200,ci=new Set(["pending","approved","rejected","failed","withdrawn","expired","superseded"]);a(Co,"matchesStatusFilter");a(Sc,"isProposalStatus");Tc={status:[...ci].join(", "),limit:`integer 1\u2013${Ut}`,offset:"integer \u2265 0",mine:"true, false, 1, 0",flowId:"one flow tab id"};a(Bt,"invalidFilterError");a(fr,"singleQueryValue");a(Pc,"parseIntegerFilter");a(Eo,"isUnset");a(Rc,"parseMineFilter");a(Io,"parseStatusFilter");a(li,"parseRangeFilter");a(mr,"parseListProposalsQuery");a(ft,"listPageFields");a(_c,"applyListPagination");a(qt,"listAccessibleProposals");a(De,"getAccessibleProposal");a(Lc,"accessLinkActor");a(Wt,"withdrawMyProposal");a(Cc,"conflictsForProposal");a(ue,"presentAccessibleProposalForApi");y=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(pe,"listFlows");a(xe,"getFlow");a(Ec,"resolveExpectedRev");a(ui,"assertNodesArrayOrAbsent");a(Ic,"materializePayloadFromNodes");a(Nc,"resolveReplaceFlowPayload");a(Fc,"assertSafeRevAndFlowId");a(Ye,"submitReplaceFlowProposal");a(O,"aiOpsErrorToHttp")});function No(e){return e==="get_link"?86400:2592e3}function Fo(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 mt,Ln=U(()=>{"use strict";a(No,"maxTtlSecondsForKind");a(Fo,"ttlCapErrorMessage");mt="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 P(e,t,n,r,o=Jt,s="error"){return{code:e,status:t,meaning:n,hint:r,channels:o,kind:s}}function Cn(e){return $c.get(e)?.hint}function pi(e){return e.replace(/\\/g,"\\\\").replace(/\|/g,"\\|")}function fi(){let e=a(s=>`| \`${s.code}\` | ${s.status} | ${pi(s.meaning)} | ${pi(s.hint)} | ${s.channels.join(", ")} |`,"row"),t=["| Code | HTTP | Meaning | Next step | Channels |","| --- | --- | --- | --- | --- |"],n=hr.filter(s=>s.kind==="error"),r=hr.filter(s=>s.kind==="warning"),o=hr.filter(s=>s.kind==="status");return[...t,...n.map(e),"","**Warnings** (in `warnings[]` on a successful propose; never blocking):","",...t,...r.map(e),"","**Auto-closed proposal statuses.** Pending proposals are closed automatically: `expired` when the live flow rev moves past their `expectedRev`, `superseded` when another proposal on the same flow is approved. Submit, get, and list responses include `autoClosedCount` when that request auto-closed proposals (omitted when zero).","",...t,...o.map(e)].join(`
7
- `)}var Jt,zt,Oo,Oc,Gt,hr,$c,gr=U(()=>{"use strict";Ln();Jt=["GET","REST","MCP"],zt=["GET"],Oo=["REST","MCP"],Oc=["GET","REST"],Gt=["admin"];a(P,"e");hr=[P("access_link_missing",401,"No Bearer / path token","Ask the user for a new access link of the right kind"),P("bearer_scheme_required",401,"Auth used Basic (or non-Bearer)","Send `Authorization: Bearer <token>`",Oo),P("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`)"),P("access_link_invalid",401,"Unknown, revoked, or cleared after restart","Ask the user for a new access link"),P("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"),P("ai_features_disabled",403,"Plugin AI features off","Owner enables AI Access / `adminAuth`"),P("auth_required",401,"Admin session required","Sign in to the Node-RED editor",Gt),P("insufficient_permissions",403,"Admin lacks required scope","Need `*` for link mint / approve",Gt),P("invalid_link_kind",400,"Unknown `kind` on create","Use `get_link`, `rest`, or `mcp`",Gt),P("invalid_ttl",400,"`ttlSeconds` is zero, negative, not a number, or over the cap for the kind",mt,Gt),P("access_link_limit",409,"Too many active links","Revoke an existing link first",Gt),P("access_link_not_found",404,"Unknown link id on revoke","List links and use an existing id",Gt),P("link_create_not_supported",403,"Attempted mint under a GET link","Admin mints via `POST /mcp/api/v1/links` only",zt),P("rate_limited",429,"Per-IP or per-token limit","Wait `retryAfter` / `Retry-After`"),P("payload_too_large",413,"Body / proposal over ~1.5MB","Shrink content"),P("invalid_json",400,"Malformed JSON body","Fix Content-Type / body",Oo),P("unsupported_media_type",415,"POST body is not `application/json`","Send `Content-Type: application/json`",Oo),P("url_too_long",414,"GET URL over soft cap (~8KB)","Use chunked drafts or REST/MCP",zt),P("not_found",404,"Unknown path or resource","Re-read docs / discovery"),P("missing_query",400,"Required search `q` missing","Pass `?q=`",Oc),P("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"),P("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"]),P("invalid_diff_format",400,"Unknown `format` on proposal diff","Use `format=summary` (default) or `format=full`"),P("confirm_required",400,"GET withdraw without `confirm=1`","Repeat the request with `confirm=1`",zt),P("missing_expected_rev",400,"Proposal submitted without `expectedRev` / `rev`","Read the flow and pass its `rev`"),P("version_mismatch",409,"Stale `rev` / `expectedRev`","Re-read flows and resubmit"),P("proposal_not_found",404,"Unknown proposal id","List proposals and use an existing id"),P("proposal_not_pending",409,"Action needs a pending proposal","Only pending can withdraw / decide"),P("proposal_not_owned",403,"Withdraw of another token\u2019s proposal","Only the creating token can withdraw"),P("validation_failed",400,"Several validation errors at once (message `N validation errors`)","Fix every entry in errors[] and resubmit."),P("duplicate_node_id",400,"The same node id appears more than once in a full tab or batch","Give every node a unique id"),P("missing_node_id",400,"Node / op without an id","Include a non-empty string id"),P("missing_node_type",400,"Node without a `type`","Set `type` to an installed palette type name"),P("invalid_node_type",400,"`type` is not a string","Use the palette type name exactly"),P("invalid_nodes",400,"`flow.nodes` / `flow.configs` not an array","Send arrays (use `[]` to clear)"),P("invalid_node",400,"Node entry is not an object","Send `{ id, type, \u2026 }` objects"),P("invalid_wires",400,"Bad wires shape, missing target, or more ports than outputs","Wire only to existing node ids / ports"),P("z_mismatch",400,"Node `z` differs from the flow id","Set `z` to the tab id or omit it"),P("node_already_exists",400,"`add` op for an id already in the flow","Use `set` or a new id"),P("protected_property",400,"Tried to change id / type / z","Omit id, type, and z from edits"),P("node_not_found",404,"Unknown node (single error only; several errors \u2192 `validation_failed`)","Re-read the flow"),P("field_not_found",404,"Unknown field on a node","Re-read the node"),P("text_not_found",400,"`replace_text` find had no match","Re-read field; find is exact"),P("ambiguous_match",409,"`replace_text` matched more than once","Pass `occurrence` (1-based) or unique find"),P("unknown_node_type",400,"Proposal uses a type that is not installed","List node-types / palette search; ask user to install"),P("node_type_not_found",404,"Unknown type on node-types detail","List types or search palette"),P("chunk_conflict",409,"Same draft seq re-sent with different data","Re-send identical data or start a new draft",zt),P("missing_chunks",400,"Draft submit with gaps in seq","Upload every seq from 0 through max",zt),P("draft_hash_mismatch",400,"Draft sha256 does not match assembled bytes","Send the hex sha256 of the assembled UTF-8",zt),P("missing_config_node",201,"Warning: node has no (or an unresolved) config node yet","Configure before deploy if needed; see `candidates`",Jt,"warning"),P("unwired_node",201,"Warning: node has outputs but no wires","Wire it, or ignore while scaffolding",Jt,"warning"),P("incomplete_wires",201,"Warning: fewer wire ports than outputs","Add ports if outputs should connect",Jt,"warning"),P("expired",200,"Status: pending proposal auto-closed because the live flow rev moved past `expectedRev`","Re-read the flow and submit a new proposal",Jt,"status"),P("superseded",200,"Status: pending proposal auto-closed because another proposal on the same flow was approved","Re-read the flow and submit a new proposal if still needed",Jt,"status"),P("upstream_error",502,"Node-RED runtime/flows error","Retry; check Node-RED logs"),P("internal_error",500,"Unexpected server error","Retry; check Node-RED logs")],$c=new Map(hr.map(e=>[e.code,e]));a(Cn,"registryHint");a(pi,"cell");a(fi,"renderGetLinkErrorTable")});function se(e,t,n={}){return $o(e,t,n)}function Mc(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 Dc(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 Qe(e,t,n,r){return e==="mcp"?t:e==="rest"?n:r}function $o(e,t,n={}){switch(e){case"version_mismatch":return Qe(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 Qe(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 Qe(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 Qe(t,"Call list_proposals and use an id from that response.","List proposals and use an id from that response.","List proposals and use an id from that response.");case"proposal_not_owned":return"Only the access link that created a pending proposal can withdraw it.";case"proposal_not_pending":return"Only pending proposals can be withdrawn or decided.";case"confirm_required":return"Add confirm=1 to the withdraw URL (required so link prefetchers cannot withdraw).";case"invalid_wires":return Mc(t,n.cause);case"node_type_not_found":case"unknown_node_type":return Dc(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 Qe(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 Qe(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 Qe(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 Qe(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 _n(e,t,n={}){let r=n.canonicalizeMessage!==!1,o=null;if(e instanceof y?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,...e.details??{}}}:e instanceof J?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,expectedRev:e.expectedRev,currentRev:e.currentRev}}:e instanceof z?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,flowId:e.flowId}}:e instanceof ne||e instanceof Ot||e instanceof te||e instanceof X?o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}:(e instanceof Ce||e instanceof be)&&(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=Oe[s];i&&(o.body.error=i)}if(typeof o.body.hint!="string"||!o.body.hint){let i=$o(s,t,{cause:o.body.cause})??Cn(s);i&&(o.body.hint=i)}return o}function mi(e,t,n={}){let o={error:n.error??Oe[t]??"Request failed",code:t,...n.details??{}},s=$o(t,e,{cause:o.cause})??Cn(t);return s&&(o.hint=s),{statusCode:n.statusCode??400,body:o}}var Oe,Fe=U(()=>{"use strict";ve();Ge();G();K();gr();Oe={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(se,"channelErrorHint");a(Mc,"invalidWiresHint");a(Dc,"nodeTypeHint");a(Qe,"pickChannel");a($o,"hintFor");a(_n,"mapErrorForChannel");a(mi,"buildChannelError")});var vm={};ds(vm,{ACCESS_LINK_INVALID_MESSAGE:()=>st,ACCESS_LINK_MISSING_MESSAGE:()=>le,AccessLinkStore:()=>At,EDITOR_SIDEBAR_ASSET_VERSION:()=>Xt,MAX_PROPOSAL_JSON_BYTES:()=>j,SettingsStore:()=>Lt,acceptedMediaTypes:()=>wd,areAiFeaturesEnabled:()=>ge,bindSettingsStore:()=>Jn,createPluginServices:()=>Vr,default:()=>bm,getStorageDir:()=>Hr,isAdminAuthEnabled:()=>W,isMcpStreamRequest:()=>bd,packageVersion:()=>Vt,registerRoutes:()=>vd,renderEditorSidebarJs:()=>yr,renderEditorSidebarLoaderHtml:()=>wi,requireAccessLink:()=>Nt,resolveAiAccessSettings:()=>he});module.exports=Pd(vm);var yd=require("fs");var Un=require("fs"),un=require("path");G();function Bn(e){let t=(0,un.join)(e,".ai-access");return{root:t,proposals:(0,un.join)(t,"proposals"),logs:(0,un.join)(t,"logs"),settings:(0,un.join)(t,"config.json")}}a(Bn,"resolveAiAccessPaths");function Qr(e){(0,Un.mkdirSync)(e,{recursive:!0,mode:Yr});try{(0,Un.chmodSync)(e,Yr)}catch{}}a(Qr,"mkdirSecure");function fs(e){Qr(e.root),Qr(e.proposals),Qr(e.logs)}a(fs,"ensureAiAccessDirs");it();_e();function Yn(e){let t=ae(e);return`auth-tokens${(t==="/"?"":t).replace(/\//g,"-")}`}a(Yn,"editorAuthTokenKey");_e();Fe();function jc(e){if(!e)return!1;let t=e.split(";")[0]?.trim().toLowerCase()??"";return t==="application/json"||t.endsWith("+json")}a(jc,"isJsonContentType");function Hc(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(Hc,"hasRequestBody");function je(e="rest"){return(t,n,r)=>{let o=t.headers["content-type"];if(jc(o)){r();return}if(!o&&!Hc(t)){r();return}let s=mi(e,"unsupported_media_type",{statusCode:415});n.status(s.statusCode).json(s.body)}}a(je,"requireJsonContentType");var Uc="text/plain; charset=utf-8",Bc="text/html; charset=utf-8",qc="default-src 'none'; base-uri 'self'; form-action 'none'; frame-ancestors 'none'";function Wc(e){let t=e.query?.format;return typeof t=="string"?t.toLowerCase():void 0}a(Wc,"formatParam");function zc(e){let t=Wc(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(zc,"chooseGetLinkRepresentation");function hi(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}a(hi,"escapeHtml");var Gc=/(?<![A-Za-z0-9_.~\-/])\.\/[A-Za-z0-9._~\-/]+(?:\?[A-Za-z0-9._~\-=&%]+)?/g;function Jc(e){return e.replace(Gc,(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(Jc,"linkifyEscaped");function Kc(e,t){return`<!doctype html>
8
- <html><head><meta charset="utf-8">${t?`<base href="${hi(t)}">`:""}<title>Node-RED AI Access</title></head><body><pre>${Jc(hi(e))}</pre></body></html>
9
- `}a(Kc,"renderGetLinkHtml");function Vc(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(Vc,"getLinkBaseHref");function Xc(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}a(Xc,"prettyJson");function gi(){return(e,t,n)=>{let r=zc(e),o=Vc(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",Uc),s(u)):(t.setHeader("Content-Type",Bc),t.setHeader("Content-Security-Policy",qc),s(Kc(l?Xc(u):u,o)))}),n()}}a(gi,"getLinkContentNegotiation");qn();_e();function Yc(e){let t=de(e.settings.httpAdminRoot),n=qe(e.settings.httpAdminRoot),r="0.2.0-beta.4",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(Yc,"buildDiscoveryIndex");function Mo(e){return(t,n)=>{n.status(200).json(Yc(e))}}a(Mo,"discoveryIndexHandler");function yi(){return(e,t)=>{t.status(404).json({error:"Not found",code:"not_found"})}}a(yi,"mcpApiNotFoundHandler");var Kt;function Vt(){return Kt||(Kt="0.2.0-beta.4",Kt)}a(Vt,"packageVersion");var Xt=Vt();function Qc(){return`#red-ui-sidebar-ai-access {
3
+ `,o=Buffer.from(r,"utf8");if(o.byteLength>t)throw new Y(n.kind,o.byteLength,t);return{bytes:o}}function Ds(e,t){let n=(0,wn.resolve)(t),r=(0,wn.resolve)(e);if(r!==n&&!r.startsWith(n+wn.sep))throw new Error("Path escapes .ai-access storage root");return r}function Ot(e,t,n){let r=Ds(t,e),o=`${r}.${process.pid}.tmp`;(0,Be.writeFileSync)(o,n.bytes,{mode:er}),(0,Be.renameSync)(o,r),(0,Be.chmodSync)(r,er)}function js(e,t,n){let r=Ds(t,e);(0,Be.appendFileSync)(r,n.bytes,{mode:er});try{(0,Be.chmodSync)(r,er)}catch{}}var Be,wn,ko,er,H,Ms,Y,G=U(()=>{"use strict";Be=require("fs"),wn=require("path"),ko=448,er=384,H=15e5,Ms=8192,Y=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(Je,"encodeTrustedJson");a(Ds,"assertUnderAiAccess");a(Ot,"writeTrustedAtomic");a(js,"appendTrusted")});function vn(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 Ll(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 Us(e){return(t,n,r)=>{if(!_l.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=vn(t.get("origin")),d=i===void 0?void 0:e().find(c=>c===i),l=d!==void 0;if(Ll(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",Cl),t.method==="OPTIONS"&&t.get("access-control-request-method")){n.setHeader("Access-Control-Allow-Methods",Pl),n.setHeader("Access-Control-Allow-Headers",El),n.setHeader("Access-Control-Max-Age",String(600)),n.status(204).end();return}s=!1,r()}}function Bs(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 _l,Pl,El,Cl,rr=U(()=>{"use strict";_l=/^\/mcp(?:\/?$|\/(?:a(?:\/|$)|api(?:\/|$)|rpc\/?$))/,Pl="GET, POST, DELETE, OPTIONS",El="Authorization, Content-Type, Accept, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID",Cl="Mcp-Session-Id, WWW-Authenticate, Retry-After, RateLimit, RateLimit-Policy, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset";a(vn,"canonicalOrigin");a(Ll,"appendVary");a(Us,"aiCorsMiddleware");a(Bs,"installAiCorsMiddleware")});function sr(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 An(e){if(!Array.isArray(e))return;let t=[];for(let n of e){if(typeof n!="string")continue;let r=n.trim().toUpperCase();if(!(!or.test(r)||t.includes(r))&&(t.push(r),t.length>=kn))break}return t}var te,ct,or,kn,Sn=U(()=>{"use strict";te="[REDACTED]",ct=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"]),or=/^[A-Za-z0-9_-]{2,40}$/,kn=100;a(sr,"tokenizeName");a(An,"normalizeSecretNamePatterns")});function Nl(e){return e==="nodes"||e==="flows"||e==="full"}function ir(e,t){if(!e||typeof e!="object"||Array.isArray(e))return{};let n=e,r={};if(typeof n.enabled=="boolean"&&(r.enabled=n.enabled),typeof n.allowWithoutAdminAuth=="boolean"&&(r.allowWithoutAdminAuth=n.allowWithoutAdminAuth),typeof n.defaultLinkTtlSeconds=="number"&&Number.isFinite(n.defaultLinkTtlSeconds)&&n.defaultLinkTtlSeconds>0&&(r.defaultLinkTtlSeconds=Math.floor(n.defaultLinkTtlSeconds)),Nl(n.defaultDeploymentType)&&(r.defaultDeploymentType=n.defaultDeploymentType),typeof n.maxActiveLinks=="number"&&Number.isFinite(n.maxActiveLinks)&&n.maxActiveLinks>0&&(r.maxActiveLinks=Math.floor(n.maxActiveLinks)),typeof n.publicBaseUrl=="string")try{r.publicBaseUrl=Dt(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=vn(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=An(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 Fl(e,t){if(!(0,Tn.existsSync)(e))return{};try{let n=(0,Tn.readFileSync)(e,"utf8");return ir(JSON.parse(n),t)}catch(n){return t?.(`AI Access: config.json is corrupt or unreadable; defaults are used (${String(n)})`),{}}}function ar(e,t){let n={},r={...xn};for(let s of Object.keys(xn).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]??xn[s]});let o={...r,sources:n};return e.oauth!==void 0||t.oauth!==void 0?(o.oauth={...$t,...t.oauth??{},...e.oauth??{}},n.oauth=e.oauth!==void 0?"settings.js":"disk"):(o.oauth={...$t},n.oauth="default"),o}function xo(e,t){let n=ir(e??{},t);for(let o of Object.keys(xn).filter(s=>s!=="oauth"))e&&Object.hasOwn(e,o)&&n[o]===void 0&&Object.assign(n,{[o]:xn[o]});let r=e?.oauth;if(r&&typeof r=="object"&&!Array.isArray(r)){let o=n.oauth??={};for(let s of Object.keys($t))Object.hasOwn(r,s)&&o[s]===void 0&&(t?.(`AI Access: settings.js aiAccess.oauth.${s} is invalid; using the default (${String($t[s])})`),Object.assign(o,{[s]:$t[s]}))}return n}var Tn,So,Il,$t,xn,Mt,dr=U(()=>{"use strict";Tn=require("fs"),So=require("path");ce();rr();Sn();G();Il=16384,$t={enabled:!1,accessTokenTtlSec:3600,refreshIdleTtlSec:2592e3,refreshMaxLifetimeSec:7776e3,dcrMaxClients:5},xn={enabled:!0,allowWithoutAdminAuth:!1,defaultLinkTtlSeconds:14400,maxActiveLinks:10,defaultDeploymentType:"nodes",publicBaseUrl:"",corsAllowedOrigins:[],rateLimitWindowMs:6e4,rateLimitMaxPerIp:600,rateLimitMaxPerToken:300,secretNamePatterns:[...ct],oauth:{...$t}};a(Nl,"isDeploymentType");a(ir,"sanitizePartial");a(Fl,"readDiskFile");a(ar,"mergeAiAccessSettings");a(xo,"settingsJsPartial");Mt=class{constructor(t,n,r){this.paths=t;this.filePath=(0,So.join)(t.root,"config.json"),this.settingsJs=xo(n,r),this.disk=Fl((0,Tn.existsSync)(this.filePath)?this.filePath:(0,So.join)(t.root,"settings.json"),r),this.cached=ar(this.settingsJs,this.disk)}static{a(this,"SettingsStore")}filePath;disk;settingsJs;cached;getEffective(){return this.cached}getDisk(){return{...this.disk}}update(t){let n=ir(t);for(let r of Object.keys(this.settingsJs))delete n[r];return this.persist(n),this.disk=n,this.cached=ar(this.settingsJs,this.disk),this.cached}patch(t,n=[]){let r=ir(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=Je(t,Il,{kind:"settings",pretty:!0});Ot(this.paths.root,this.filePath,n)}}});function cr(e){lr=e}function qs(){return lr}function z(e){return e.adminAuth!=null&&e.adminAuth!==!1}function ve(e){return lr?lr.getEffective():ar(xo(e.aiAccess),{})}function ke(e){let t=ve(e);return t.enabled?z(e)?!0:t.allowWithoutAdminAuth:!1}var lr,ue=U(()=>{"use strict";dr();lr=null;a(cr,"bindSettingsStore");a(qs,"getSettingsStore");a(z,"isAdminAuthEnabled");a(ve,"resolveAiAccessSettings");a(ke,"areAiFeaturesEnabled")});function pe(e){let t=`/${e??""}`.replace(/\/{2,}/g,"/");for(;t.length>1&&t.endsWith("/");)t=t.slice(0,-1);return t}function fe(e){let t=pe(e);return t==="/"?"/mcp/api/v1":`${t}/mcp/api/v1`}function Rn(e){let t=pe(e);return t==="/"?"/ai-access":`${t}/ai-access`}function Ve(e){let t=pe(e);return t==="/"?"/mcp/rpc":`${t}/mcp/rpc`}function Ol(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 Ye(e){if(e.auth)return e.auth.scopes.includes("propose")?"propose":e.auth.scopes.includes("read")?"read":null;let t=Ol(e);return t.includes("*")?"*":t.includes("read")?"read":null}function $l(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 zs(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 Ae(e,t){let n="read";return(r,o,s)=>{if(!z(e.settings)){s();return}let i=t==="api"?zs(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(!Ye(r)){o.status(401).json({error:"Insufficient permissions",code:"insufficient_permissions"});return}s()})}}function ur(e,t){let n=z(e.settings),r=ve(e.settings),o=ke(e.settings),s=null;return n?s=Ye(t):o&&(s="*"),{adminAuthEnabled:n,aiFeaturesEnabled:o,allowWithoutAdminAuth:r.allowWithoutAdminAuth,permission:s,capabilities:$l(s,o)}}function jt(e){return(t,n,r)=>{if(!ke(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 _n(e){return(t,n,r)=>{if(!z(e.settings)){if(ke(e.settings)){r();return}n.status(403).json({error:"AI Access features are disabled",code:"ai_features_disabled"});return}if(Ye(t)!=="*"){n.status(403).json({error:"Full admin permission (*) is required for this action",code:"insufficient_permissions"});return}r()}}function Ml(e){return e.startsWith("aia_")}function Dl(e){return Ml(e)?!0:e.length<24}function jl(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 Ws(e,t,n,r){if(!z(e.settings)){r();return}let o={error:me,code:Ie},s=zs(n,o,X);e.auth.needsPermission("read")(t,n,i=>{if(s(),i){r(i);return}if(!n.headersSent){if(!t.user||!Ye(t)){n.setHeader("WWW-Authenticate",X),n.status(401).json(o);return}r()}})}function Gs(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?(Cn(t,r,l.link.kind),"wrong_kind"):(e.accessLink=l.link,"ok");if(l.status==="expired")return En(e,t,o,l.expiresAt),"invalid";if(Dl(d)){t.setHeader("WWW-Authenticate",X);let c=jl(d);return t.status(401).json({error:ut,code:Pn,...c?{hint:c}:{}}),"invalid"}return"continue"}function Ks(e,t,n="rest"){return(r,o,s)=>{if(!ke(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=Gs(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||Ws(e,r,o,s)}}function Js(e,t,n="rest"){return(r,o,s)=>{let i=Gs(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||Ws(e,r,o,s)}}var Le=U(()=>{"use strict";pt();ue();a(pe,"normalizeHttpAdminRoot");a(fe,"mcpApiBase");a(Rn,"dashboardPath");a(Ve,"mcpRpcPath");a(Ol,"scopesFromRequest");a(Ye,"resolvePermission");a($l,"capabilitiesFor");a(zs,"interceptNeedsPermissionUnauthorized");a(Ae,"requireAdminSession");a(ur,"describeAccess");a(jt,"requireAiFeatures");a(_n,"requireStarPermission");a(Ml,"looksLikeAccessLinkToken");a(Dl,"looksLikeFailedAccessLinkAttempt");a(jl,"truncatedTokenHint");a(Ws,"continueAsAdminSessionOrAccessLinkMissing");a(Gs,"tryAccessLinkBearer");a(Ks,"requireAdminReadOrAccessLink");a(Js,"requireAdminSessionOrAccessLink")});function Hl(e,t){let n=pe(e);return`${n==="/"?"/mcp/a":`${n}/mcp/a`}/${t}`}function Ul(e){return!!e.app.get("trust proxy")}function Vs(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 Bl(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 ft(e){try{let n=new URL(`http://${e.includes(":")&&!e.startsWith("[")?`[${e}]`:e}`).hostname.replace(/^\[|\]$/g,"");if(!(0,To.isIP)(n))return;if(n.startsWith("::ffff:")){let r=n.slice(7);if((0,To.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 Dt(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 pr(e){Ro=Bl(e)}function Xs(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],!Wl.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)||!zl.test(t)&&!ql.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 Qs(e){let t=e?.trim().toLowerCase();return t==="http"||t==="https"?t:void 0}function Gl(e){let t=e.socket;return t?.encrypted?"https":t?"http":Qs(e.protocol)??"http"}function _o(e){if(Ro)return Ro;let t=Ul(e),n=t?Vs(e,"x-forwarded-host"):void 0,r=e.get("host")?.trim(),o=Xs(n)?n:Xs(r)?r:void 0;if(!o)return;let s=(t?Qs(Vs(e,"x-forwarded-proto")):void 0)??Gl(e);try{return new URL(`${s}://${o}`).origin}catch{return}}function Ln(e,t){let n=_o(e);return n?`${n}${t}`:t}function ei(e,t,n){return Ln(e,Hl(t,n))}function Po(e){return e.replace(/\/mcp\/a\/[^/?#]+/gi,"/mcp/a/<redacted>")}var To,Ht,Zs,Ro,Ys,ql,zl,Wl,ce=U(()=>{"use strict";To=require("net");Le();Ht=8192,Zs=1800*1e3;a(Hl,"mcpAccessLinkPath");a(Ul,"isTrustProxyEnabled");a(Vs,"firstForwardedValue");a(Bl,"normalizePublicBaseUrl");a(ft,"canonicalizeIp");a(Dt,"validateSettingsPublicBaseUrl");a(pr,"bindPublicBaseUrl");Ys="[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?",ql=new RegExp(`^${Ys}(?:\\.${Ys})*$`),zl=/^(?:(?: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)$/,Wl=/^\[[0-9A-Fa-f:.]+\]$/;a(Xs,"isValidHostHeader");a(Qs,"validScheme");a(Gl,"connectionScheme");a(_o,"requestPublicOrigin");a(Ln,"absolutizePath");a(ei,"buildAccessLinkUrl");a(Po,"redactAccessLinkTokenInPath")});function Zl(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 Eo(e,t,n,r){e.setHeader("WWW-Authenticate",X),e.status(401).json({error:t,code:n,...r?{hint:r}:{}})}function Ql(e){let t=e??"";for(;t.endsWith("/");)t=t.slice(0,-1);return`${t}/ai-access#links`}function ec(e,t,n,r){let o=Ln(e,Ql(t));return{error:Kl,code:Vl,...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 En(e,t,n,r,o){t.setHeader("WWW-Authenticate",X),t.status(401).json(ec(e,n,r,o))}function tc(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 fr(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 rc(e,t){let n=nc;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: ${fr("get_link")}`:e==="rest"?`This REST endpoint requires a rest token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${fr("rest")}`:`This MCP endpoint requires an mcp token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${fr("mcp")}`}function Cn(e,t,n){e.setHeader("WWW-Authenticate",X),e.status(403).json({error:`Access link kind mismatch: expected ${t}, got ${n}`,code:Xl,expectedKind:t,actualKind:n,hint:rc(t,n),tellUser:fr(t)})}function Ut(e,t,n){return(r,o,s)=>{if(!ke(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=Zl(r.headers.authorization);if(i.kind==="basic"){Eo(o,Jl,Yl);return}if(i.kind==="missing"||i.kind==="other"){Eo(o,me,Ie);return}let d=t.validateResult(i.token);if(d.status==="expired"){En(r,o,e.settings.httpAdminRoot,d.expiresAt);return}if(d.status!=="active"){Eo(o,ut,Pn,tc(i.token));return}if(d.link.kind!==n){Cn(o,n,d.link.kind);return}r.accessLink=d.link,s()}}var ut,Kl,me,Jl,X,Ie,Pn,Vl,Yl,Xl,nc,pt=U(()=>{"use strict";ce();ue();ut="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.",Kl="Access link token has expired. Ask the user for a new access link.",me="Access link Bearer token is missing. Ask the user for a new access link and send Authorization: Bearer <token>.",Jl="Authorization must use the Bearer scheme (Authorization: Bearer <access-link-token>), not Basic. Ask the user for a new access link.",X='Bearer realm="node-red-ai-access", charset="UTF-8"',Ie="access_link_missing",Pn="access_link_invalid",Vl="token_expired",Yl="bearer_scheme_required",Xl="wrong_token_kind";a(Zl,"parseAuthorizationHeader");a(Eo,"sendUnauthorized");a(Ql,"accessLinksPagePath");a(ec,"tokenExpiredBody");a(En,"sendTokenExpired");a(tc,"truncatedBearerHint");nc="ask the owner: Node-RED /ai-access \u2192 Access tokens \u2192 create kind=rest/mcp (or get_link)";a(fr,"whatToTellUserWrongTokenKind");a(rc,"wrongTokenKindHint");a(Cn,"sendWrongTokenKind");a(Ut,"requireAccessLink")});var K,W,Se,Ne,hr,xe=U(()=>{"use strict";K=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}},W=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}},Se=class extends Error{static{a(this,"AdapterUnavailableError")}code="adapter_unavailable";statusCode=502;constructor(t){super(t),this.name="AdapterUnavailableError"}},Ne=class extends Error{static{a(this,"PluginMisconfiguredError")}code="plugin_misconfigured";statusCode=502;constructor(t){super(t),this.name="PluginMisconfiguredError"}},hr="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 Fe(e,t){if(!e||!oc.test(e)||e.includes(".."))throw new Error(`Invalid ${t}`);return e}function gr(e){if(!e||!sc.test(e)||e.includes(".."))throw new Error("Invalid proposal id");return e}function Co(e,t){let n=(0,mt.basename)(t);if(!n||n!==t||n.includes(".."))throw new Error("Invalid file name");let r=(0,mt.resolve)(e),o=(0,mt.resolve)(r,n);if(o!==r&&!o.startsWith(r+mt.sep))throw new Error("Path escapes storage directory");return o}function ti(e,t=1e5){if(typeof e=="string")return e.replace(/\0/g,"").slice(0,t)}function ht(e){let t=e&&typeof e=="object"&&!Array.isArray(e)?e:{},n={};for(let o of ic){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=ti(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>H)throw new Y(`flow.${o}`,d,H);n[o]=JSON.parse(i)}let r=Buffer.byteLength(JSON.stringify(n),"utf8");if(r>H)throw new Y("flow",r,H);return n}function ni(e){let t={};for(let n of ac){if(!(n in e))continue;let r=e[n];if(dc.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=ti(r,2e3);o!==void 0&&(t[n]=o)}return t}var mt,oc,sc,ic,ac,dc,yr=U(()=>{"use strict";mt=require("path");G();oc=/^[A-Za-z0-9_.-]+$/,sc=/^prop-[A-Za-z0-9_-]+$/,ic=["id","type","label","disabled","info","env"],ac=["proposalId","flowId","expectedRev","resultRev","reason","code","currentRev","title","displayName","flowLabel","linkId","label","expiresAt","createdAt","ttlSeconds","kind","redactions","restoredRedactions","operation"],dc=new Set(["ttlSeconds","redactions","restoredRedactions"]);a(Fe,"assertSafeSegment");a(gr,"assertSafeProposalId");a(Co,"resolveUnderDir");a(ti,"sanitizeString");a(ht,"sanitizeFlowTab");a(ni,"sanitizeAuditDetails")});function vr(e,t){return`oauth-${(0,br.createHash)("sha256").update(`${e}\0${t}`).digest("hex")}`}function gt(e){return e.kind==="oauth"?vr(e.clientId,e.user):e.linkId}function lc(e){return e==="pending"||e==="approved"||e==="rejected"||e==="failed"||e==="withdrawn"||e==="expired"||e==="superseded"}function uc(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 ri(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e.slice(0,cc)){if(!n||typeof n!="object"||Array.isArray(n))continue;let r=n;if(typeof r.code!="string"||!r.code||typeof r.error!="string"||!r.error)continue;let o={code:r.code.replace(/\0/g,"").slice(0,80),error:r.error.replace(/\0/g,"").slice(0,500)};typeof r.nodeId=="string"&&r.nodeId&&(o.nodeId=r.nodeId.replace(/\0/g,"").slice(0,200)),typeof r.type=="string"&&r.type&&(o.type=r.type.replace(/\0/g,"").slice(0,200)),typeof r.property=="string"&&r.property&&(o.property=r.property.replace(/\0/g,"").slice(0,200)),typeof r.configType=="string"&&r.configType&&(o.configType=r.configType.replace(/\0/g,"").slice(0,200)),Array.isArray(r.candidates)&&(o.candidates=r.candidates.filter(s=>typeof s=="string"&&s.length>0).slice(0,20).map(s=>s.replace(/\0/g,"").slice(0,200))),t.push(o)}return t.length>0?t:void 0}var br,Xe,oi,se,ne,Bt,In,cc,wr,Ze=U(()=>{"use strict";br=require("crypto"),Xe=require("fs"),oi=require("path");yr();G();a(vr,"oauthOwnerId");a(gt,"proposalOwnerId");se=class extends Error{static{a(this,"ProposalNotFoundError")}code="proposal_not_found";statusCode=404;constructor(t){super(`Proposal '${t}' was not found`),this.name="ProposalNotFoundError"}},ne=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"}},Bt=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"}},In=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(lc,"isProposalStatus");cc=50;a(uc,"sanitizeRestoredSecrets");a(ri,"sanitizeProposalWarnings");wr=class{constructor(t,n={}){this.paths=t;this.now=n.now??(()=>Date.now()),this.randomId=n.randomId??(()=>(0,br.randomBytes)(12).toString("hex")),this.maxProposalBytes=n.maxProposalBytes??H}static{a(this,"ProposalStore")}inFlight=new Set;now;randomId;maxProposalBytes;fileFor(t){let n;try{n=gr(t)}catch{throw new se(t)}return Co(this.paths.proposals,`${n}.json`)}toDiskRecord(t){let n=gr(t.id),r=Fe(String(t.expectedRev).slice(0,200),"expectedRev"),o=Fe(String(t.payload.flowId).slice(0,200),"flowId"),s=ht(t.payload.flow),i=t.baseFlow!==void 0?ht(t.baseFlow):void 0,d=t.createdBy.kind==="oauth"?{kind:"oauth",clientId:Fe(String(t.createdBy.clientId).slice(0,200),"clientId"),user:String(t.createdBy.user).replace(/\0/g,"").slice(0,200)}:{kind:"access-link",linkId:Fe(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:lc(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=ri(t.warnings);c&&(l.warnings=c);let u=uc(t.restoredSecrets);if(u&&(l.restoredSecrets=u),t.decidedBy)if(t.decidedBy.kind==="access-link"){let p={kind:"access-link",linkId:Fe(String(t.decidedBy.linkId).slice(0,200),"linkId")};typeof t.decidedBy.label=="string"&&(p.label=t.decidedBy.label.replace(/\0/g,"").slice(0,200)),l.decidedBy=p}else{let p={kind:"admin"};typeof t.decidedBy.username=="string"&&(p.username=t.decidedBy.username.replace(/\0/g,"").slice(0,200)),l.decidedBy=p}return typeof t.reason=="string"&&(l.reason=t.reason.replace(/\0/g,"").slice(0,2e3)),typeof t.resultRev=="string"&&(l.resultRev=Fe(t.resultRev.slice(0,200),"resultRev")),l}writeAtomic(t,n){let r=this.fileFor(t),o=this.toDiskRecord(n),s=Je(o,this.maxProposalBytes,{kind:"proposal",pretty:!0});Ot(this.paths.root,r,s)}create(t){let n=new Date(this.now()).toISOString(),r=gr(`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=ri(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,Xe.existsSync)(n))throw new se(t);return JSON.parse((0,Xe.readFileSync)(n,"utf8"))}list(t){if(!(0,Xe.existsSync)(this.paths.proposals))return[];let n=(0,Xe.readdirSync)(this.paths.proposals).filter(o=>o.endsWith(".json")),r=[];for(let o of n){let s=(0,oi.basename)(o);if(!(s!==o||s.includes("..")))try{let i=JSON.parse((0,Xe.readFileSync)(Co(this.paths.proposals,s),"utf8"));if(t?.linkId&&gt(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 In(t);let n=this.get(t);if(n.status!=="pending")throw new ne(t,n.status);return this.inFlight.add(t),n}releaseLock(t){this.inFlight.delete(t)}markApproved(t,n,r){let o=this.get(t);return o.status="approved",o.decidedBy=n,o.resultRev=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markRejected(t,n,r){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){let o=this.get(t);if(o.status!=="pending")throw new ne(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(s.status!=="pending")throw new ne(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)}}});function si(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 kr(e,t,n){let r=e.find(d=>d.id===t);if(!r||r.type!=="tab"&&r.type!=="subflow")throw new W(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({...si(l,`flowData.nodes[${d}]`),z:t});for(let[d,l]of(n.configs??[]).entries())s.push({...si(l,`flowData.configs[${d}]`),z:t});return[...e.filter(d=>d.id!==t&&d.z!==t),o,...s]}function Nn(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 qt(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 W(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 W(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 Ar=U(()=>{"use strict";xe();a(si,"asFlowNode");a(kr,"replaceFlowTabInFlatFlows");a(Nn,"assertFlowsSnapshot");a(qt,"normalizeFlowTab")});function Fo(e){return e?pc.has(e)?!0:e.startsWith("subflow:"):!1}function Qe(e,t="node.type"){if(e==null||e==="")throw new y(`${t} is required`,"missing_node_type");if(typeof e!="string")throw new y(`${t} must be a non-empty string (got ${typeof e})`,"invalid_node_type",{details:{type:e,got:typeof e}});if(!e.trim())throw new y(`${t} is required`,"missing_node_type");return e}function bc(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 vc(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 Ac(e){return Math.max(2,Math.floor(e.length/3))}function Oo(e,t,n=5){if(typeof e!="string"||!e)return[];let r=Ac(e);return t.filter(s=>typeof s=="string"&&s!==e&&!kc.has(s)&&!s.startsWith("_")&&!s.startsWith("subflow:")).map(s=>({c:s,dist:bc(e,s),prefix:vc(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 Sc(e){return e.endsWith("/")?e.slice(0,-1):e}function pi(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,ui.createRequire)(e)}function xc(){try{let t=pi()("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}function fi(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 $o(e=wc,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 mi(e={}){if(xc())try{let n=pi()("@node-red/runtime");if(n.nodes&&typeof n.nodes.getNodeList=="function")return new No(n.nodes)}catch{}if(e.baseUrl)return new Io({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 Mo(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 Tc(e,t){let n=t.toLowerCase();return[e.type,e.module,e.category??"",e.description??""].join(`
4
+ `).toLowerCase().includes(n)}async function xr(e,t={}){let n=t.limit??fc,{byType:r}=await Mo(e),o=[...r.values()].sort((u,p)=>u.type.localeCompare(p.type)),s=t.q?.trim();s&&(o=o.filter(u=>Tc(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 Rc(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 ai(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 _c(e,t=2e3){let n=ai(e,"script");return n=ai(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 Pc(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:di(e,d,i)}return di(e,n.index,r)}catch{return null}}function di(e,t,n){let r=e.slice(t),o=r.indexOf("{");if(o<0)return{type:n};let s=Fn(r.slice(o));if(!s)return{type:n};let i=li(s,"category"),d=li(s,"color"),l=ci(s,"inputs"),c=ci(s,"outputs"),u=Cc(s),p=[];if(u){for(let g of Object.values(u))if(g&&typeof g=="object"&&!Array.isArray(g)){let k=g.type;typeof k=="string"&&k.length>0&&p.push(k)}}let f,m=Rc(e,n);m&&(f=_c(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 Ec(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 Fn(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 li(e,t){let n=new RegExp(`\\b${t}\\s*:\\s*['"]([^'"]+)['"]`);return e.match(n)?.[1]}function ci(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 Cc(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=Fn(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=Fn(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=Nc(m)}s[l]=u}return Object.keys(s).length>0?s:void 0}function Lc(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 Ic(e){return e==="_DEFAULT_"?"":e}function Sr(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=Lc(t,o+1);s+=d.char,o=d.next;continue}if(i===r)break;s+=i,o+=1}return Ic(s)}if(t.startsWith("[")){let r=hi(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=Sr(d);s.push(l);let c=Lo(d);i+=c>0?c:1}return s}if(t.startsWith("{")){let r=Fn(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=Sr(o.slice(i));d=typeof c=="string"?c:"",i+=Lo(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]=Sr(l),i+=Lo(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 Lo(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=hi(t);return n+(o?.length??1)}if(t.startsWith("{")){let o=Fn(t);return n+(o?.length??1)}let r=/^(?:-?\d+(?:\.\d+)?|true|false|null)/.exec(t);return r?n+r[0].length:n+1}function hi(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 Nc(e){return Sr(e)}function Fc(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 Do(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 y("Missing node type","missing_node_type");let r=n.includeHelp!==!1;if(Fo(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=Fc(o,t);if(!i){let l=Do(t,s);throw l&&l!==t?new y(`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 y(`Node type '${t}' was not found`,"node_type_not_found",{statusCode:404,details:{type:t,nearest:Oo(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=Pc(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=Ec(c.help)))}}catch{}return d}async function gi(e,t){let{allNames:n}=await Mo(e),r=new Set(n),o=new Set,s=a(l=>{let c=Qe(l,"node.type");if(o.has(c)||Fo(c)){o.add(c);return}if(o.add(c),r.has(c))return;let u=Do(c,n);throw u&&u!==c?new y(`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 y(`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:Oo(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 yi(e,t){let n=Qe(t,"node.type");if(Fo(n))return;let{allNames:r}=await Mo(e);if(r.includes(n))return;let o=Do(n,r);throw o&&o!==n?new y(`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 y(`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:Oo(n,r,5)}})}async function Oc(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 $c(e,t){let n=Date.now();if(Te&&n-Te.fetchedAt<yc)return Te.body;let r=Te?.etag,o=await e(t,r);if(o.status===304&&Te)return Te={...Te,fetchedAt:n,...o.etag?{etag:o.etag}:{}},Te.body;if(!o.body||!Array.isArray(o.body.modules)){if(Te)return Te.body;throw new y("Node-RED catalogue unavailable","catalogue_unavailable",{statusCode:502})}return Te={fetchedAt:n,body:o.body,...o.etag?{etag:o.etag}:{}},o.body}async function Tr(e,t,n={}){let r=t.trim();if(!r)throw new y("q query parameter is required","missing_query");let o=n.limit??mc,s=n.catalogueUrl??gc,i=n.catalogueFetcher??Oc,d=await $c(i,s),l=Array.isArray(d.modules)?d.modules:[],c=await e.listNodeSets(),u=new Set(c.flatMap(g=>{let k=[g.module,g.id];return g.id.includes("/")&&k.push(g.id.split("/")[0]??g.module),k.map(T=>T.toLowerCase())})),p=r.toLowerCase(),f=[];for(let g of l){let k=g.id.toLowerCase(),T=(g.keywords??[]).map(Ue=>String(Ue).toLowerCase()),C=(g.description??"").toLowerCase(),L=Mc(p,k,T,C);if(L<=0)continue;let N={module:g.id,installed:u.has(k)};typeof g.description=="string"&&(N.description=g.description),typeof g.version=="string"&&(N.version=g.version),typeof g.updated_at=="string"&&(N.updated=g.updated_at),typeof g.downloads=="number"&&(N.downloads=g.downloads),typeof g.url=="string"&&(N.link=g.url),f.push({item:N,score:L})}f.sort((g,k)=>k.score-g.score||(k.item.downloads??0)-(g.item.downloads??0)||g.item.module.localeCompare(k.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:hc}}function Mc(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 ui,pc,fc,mc,hc,gc,yc,wc,kc,Io,ii,No,Te,wt=U(()=>{"use strict";ui=require("module");J();pc=new Set(["tab","subflow","group","junction"]),fc=100,mc=30,hc="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.",gc="https://catalogue.nodered.org/catalogue.json",yc=3600*1e3,wc=["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(Fo,"isStructuralNodeType");a(Qe,"requireNodeTypeName");a(bc,"editDistance");a(vc,"sharedPrefixLength");kc=new Set(["unknown","tab","subflow","group"]);a(Ac,"nearestDistanceCutoff");a(Oo,"nearestNodeTypes");a(Sc,"trimTrailingSlash");a(pi,"createPluginRequire");a(xc,"isRuntimeSettingsAvailable");a(fi,"normalizeNodeSet");a($o,"createStaticNodeTypesAdapter");Io=class{static{a(this,"HttpNodeTypesAdapter")}baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=Sc(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=fi(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()}},ii={user:null,req:null},No=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(ii))}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=fi(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",...ii}));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(mi,"createNodeTypesAdapter");a(Mo,"flattenInstalledTypes");a(Tc,"matchesQuery");a(xr,"listNodeTypes");a(Rc,"extractHelpScriptBody");a(ai,"removeHtmlBlocks");a(_c,"stripHelpText");a(Pc,"parseRegisterTypeFromHtml");a(di,"parseRegisterTypeAt");a(Ec,"splitHelpSections");a(Fn,"extractBalancedObject");a(li,"matchStringProp");a(ci,"matchNumberProp");a(Cc,"parseDefaultsBlock");a(Lc,"unescapeJsStringEscape");a(Ic,"normalizeDefaultValue");a(Sr,"parseJsLiteral");a(Lo,"measureJsLiteralLength");a(hi,"extractBalancedArray");a(Nc,"parseLooseValue");a(Fc,"findSetForType");a(Do,"findCaseInsensitiveType");a(yt,"getNodeTypeDetail");a(gi,"assertKnownNodeTypes");a(yi,"assertKnownNodeType");Te=null;a(Oc,"defaultCatalogueFetcher");a($c,"loadCatalogue");a(Tr,"searchPalette");a(Mc,"paletteMatchScore")});function qe(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 y(`${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 y(t.error,t.code,{statusCode:o,details:r})}function jo(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 Dc(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 bi(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 ${Dc(c)}; node ids must be unique across all flows`,nodeId:l})}return o}function wi(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 Hc(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]);for(let r of n)if(!jc.has(r)&&JSON.stringify(e[r])!==JSON.stringify(t[r]))return!0;return!1}function Uc(e,t){let n=wi(t);if(!e)return new Set(n.keys());let r=wi(e),o=new Set;for(let[s,i]of n){let d=r.get(s);(!d||Hc(d,i))&&o.add(s)}return o}function vi(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(...jo(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{Qe(c.type,"node.type")}catch(u){u instanceof y&&n.push({code:u.code,error:u.message,...typeof c.id=="string"?{nodeId:c.id}:{}})}"wires"in c&&zt(c.wires,n,typeof c.id=="string"?c.id:void 0)}return n}function zt(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 Bc(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 qc(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 zc(e){return Array.isArray(e)&&e.some(t=>typeof t=="string"&&t.length>0)}function Wc(e,t){return t<=0?!1:!("wires"in e)||e.wires===void 0?!0:Array.isArray(e.wires)?!e.wires.some(zc):!1}async function Ho(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 Bc(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(!zt(d.wires,r,d.id))continue;let p=d.wires;if(l!==void 0){let f=qc(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&&Wc(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 Uo(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 Gc(e,t){return e.filter(n=>n.type===t).map(n=>n.id)}async function Bo(e,t,n){let r=[],o=[...t];for(let s of e){let i;try{i=Qe(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 h=p.required===!0||c.has(f),g=s[u],k=Gc(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:k});continue}o.find(C=>C.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:k})}}return r}async function ki(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(...Uo(r,n));let d=t.baseFlow?Uc(t.baseFlow,e):void 0,l=await Ho(r,t.nodeTypes,{...d?{softWarningNodeIds:d}:{}});if(s.push(...l.errors),i.push(...l.warnings),t.nodeTypes){let c=await Bo(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 jc,qo=U(()=>{"use strict";J();wt();a(qe,"throwValidationIssues");a(jo,"collectDuplicateNodeIdIssues");a(Dc,"describeLiveNode");a(bi,"collectCrossFlowDuplicateIssues");jc=new Set(["x","y","w","h","g","id","z"]);a(wi,"collectNodesMap");a(Hc,"nodeContentChanged");a(Uc,"changedNodeIds");a(vi,"collectFullFlowShapeIssues");a(zt,"assertValidWiresShape");a(Bc,"resolveNodeOutputCount");a(qc,"wireCountIssue");a(zc,"portHasTargets");a(Wc,"nodeIsUnwired");a(Ho,"collectWireValidation");a(Uo,"collectZMismatchIssues");a(Gc,"configCandidates");a(Bo,"collectConfigWarnings");a(ki,"validateFlowTab")});function et(e,t="node"){return typeof e=="string"&&e.trim()?e.trim():t}function Rr(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 Si(e,t){e[Ai]=t}function Vc(e){if(!e)return;let t=e[Ai];return t instanceof Map?t:void 0}function Yc(e){return typeof e!="string"||!e.trim()?new Set:new Set(e.split(",").map(t=>t.trim()).filter(Boolean))}function Xc(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 ie(e){let t=Yc(e.include);return Xc(e.include_payload)&&(t.add("payload"),t.add("baseFlow")),t}function q(e,t){let n={req:e};t!==void 0&&(n.httpAdminRoot=t);let r=Vc(e);return r&&(n.liveFlowLabels=r),n}function Wt(e){return typeof e.name=="string"&&e.name.trim()?e.name.trim():typeof e.label=="string"&&e.label.trim()?e.label.trim():""}function On(e){return`"${e.replace(/"/g,"'")}"`}function Gt(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 Zc(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 Qc(e,t){let n=Wt(e),r=Wt(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 zo(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&&Zc(d,i)&&o.push({before:d,after:i})}return{added:n,removed:r,modified:o}}function _r(e){return e===1?"node":"nodes"}function eu(e){return e.length===1&&e[0]?`adds 1 ${et(e[0].type)}`:`adds ${e.length} ${_r(e.length)}`}function tu(e){return`deletes ${e} ${_r(e)}`}function nu(e){return`edits ${e} ${_r(e)}`}function ru(e){let t=[];e.removed>0&&t.push(tu(e.removed));let n=e.added??[],r=e.addedCount??n.length;r>0&&t.push(n.length===r?eu(n):`adds ${r} ${_r(r)}`),(e.modified??0)>0&&t.push(nu(e.modified??0));let o=t.length?t.join(", "):"replaces nodes",s=e.flowLabel?.trim();return s?`Replace ${s}: ${o}`:`Replace flow: ${o}`}function ou(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=Wt(u);return f?`Add ${p} node ${On(f)}`:`Add ${p} node`}if(n[0]){let u=n[0],p=et(u.type),f=Wt(u);return f?`Remove ${p} node ${On(f)}`:`Remove ${p} node`}let s=r[0];if(!s)return;let i=et(s.after.type,et(s.before.type)),d=Wt(s.before),l=Wt(s.after);if(Qc(s.before,s.after)&&d&&l)return`Rename ${i} node ${On(d)} \u2192 ${On(l)}`;let c=l||d;return c?`Edit ${i} ${On(c)}`:`Edit ${i}`}function Wo(e,t){return typeof e?.label=="string"&&e.label.trim()?e.label.trim():typeof t?.label=="string"&&t.label.trim()?t.label.trim():""}function Pr(e,t){if(typeof e.flowLabel=="string"&&e.flowLabel.trim())return e.flowLabel.trim();let n=Wo(e.payload?.flow,e.baseFlow);if(n)return n;let r=e.payload?.flowId;return r&&t?.has(r)?t.get(r)??"":""}function xi(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 Go(e,t){let n=zo(Gt(e??{}),Gt(t)),r=n.added.length+n.removed.length+n.modified.length,o=Wo(t,e);if(r===0)return o?`Update ${o}`:"Update flow";if(n.removed.length>0)return ru({...o?{flowLabel:o}:{},removed:n.removed.length,added:n.added,modified:n.modified.length});if(r>Jc)return o?`${r} changes in ${o}`:`${r} changes`;let s=ou(n)??(o?`Update ${o}`:"Update flow");return xi(s,o)}function su(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 Ko(e,t){return`${e} \xB7 ${au(t)}`}function iu(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 au(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=iu(-n.getTimezoneOffset());return`${r} ${s} ${i}:${d} ${l}`}function Er(e){if(typeof e!="string")return;let t=e.replace(/\0/g,"").replace(/[\u0001-\u001F\u007F]/g," ").replace(/\s+/g," ").trim().slice(0,Kc).trim();return t.length>0?t:void 0}function du(e,t){let n=pe(t);return`${n==="/"?"/ai-access":`${n}/ai-access`}#proposal=${encodeURIComponent(e)}`}function Jo(e,t={}){let n=Pr(e,t.liveFlowLabels),r=Er(e.title),o=Go(e.baseFlow,n&&!Wo(e.payload.flow,e.baseFlow)?{...e.payload.flow,label:n}:e.payload.flow),s=zo(Gt(e.baseFlow??{}),Gt(e.payload.flow)),i=su(r,s.removed.length)?o:r??o;return xi(i,n)}function lu(e){return typeof e.storedTitle=="string"&&e.storedTitle?e.storedTitle:Er(e.title)}function ae(e,t={}){let n=Jo(e,t),r=Pr(e,t.liveFlowLabels),o=lu(e)??n,s=du(e.id,t.httpAdminRoot),i=t.req?Ln(t.req,s):s;return{...e,title:n,storedTitle:o,flowLabel:r,displayName:Ko(n,e.createdAt),reviewUrl:i,reviewPath:s}}function cu(e){let t=zo(Gt(e.baseFlow??{}),Gt(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 uu(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 he(e,t={}){let n=ae(e,t),r=t.include??new Set,o={id:n.id,status:n.status,type:n.type,title:n.title,storedTitle:n.storedTitle,displayName:n.displayName,flowLabel:n.flowLabel,reviewUrl:n.reviewUrl,reviewPath:n.reviewPath,createdAt:n.createdAt,updatedAt:n.updatedAt,expectedRev:n.expectedRev,createdBy:uu(n.createdBy),changeSummary:cu(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),e.restoredSecrets&&e.restoredSecrets.length>0&&(o.restoredSecrets=e.restoredSecrets),n.baseRevStale!==void 0&&(o.baseRevStale=n.baseRevStale),n.decidedBy!==void 0&&(o.decidedBy=n.decidedBy),n.reason!==void 0&&(o.reason=n.reason),n.resultRev!==void 0&&(o.resultRev=n.resultRev),o}function Ti(e,t={}){return e.map(n=>he(n,t))}var Kc,Jc,Ai,Re=U(()=>{"use strict";ce();Le();Kc=80,Jc=1;a(et,"nodeTypeText");Ai=Symbol.for("ai-access.liveFlowLabels");a(Rr,"liveFlowLabelsFromFlows");a(Si,"setRequestFlowLabels");a(Vc,"getRequestFlowLabels");a(Yc,"parseIncludeQuery");a(Xc,"isTruthyFlag");a(ie,"resolveIncludeFromQuery");a(q,"presentOptionsFor");a(Wt,"nodeLabel");a(On,"quoteName");a(Gt,"collectNodes");a(Zc,"nodeContentChanged");a(Qc,"isRenameOnly");a(zo,"classifyFlowChanges");a(_r,"pluralNodes");a(eu,"describeAddedClause");a(tu,"describeRemovedClause");a(nu,"describeModifiedClause");a(ru,"buildReplaceFlowTitle");a(ou,"describeOneChange");a(Wo,"flowLabelHint");a(Pr,"resolveFlowLabel");a(xi,"ensureTitleIncludesFlowName");a(Go,"autoGenerateProposalTitle");a(su,"shouldRefreshDisplayTitle");a(Ko,"buildProposalDisplayName");a(iu,"formatUtcOffset");a(au,"formatProposalSubmittedAt");a(Er,"sanitizeProposalTitle");a(du,"buildProposalReviewPath");a(Jo,"resolveProposalTitle");a(lu,"storedTitleOf");a(ae,"presentProposal");a(cu,"buildChangeSummary");a(uu,"presentCreatedByForApi");a(he,"presentProposalForApi");a(Ti,"presentProposalsForApi")});function Ri(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 fu(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let o of n)pu.has(o)||JSON.stringify(e[o])!==JSON.stringify(t[o])&&r.push(o);return r.sort()}function _i(e){let t=Ri(e.baseFlow??{}),n=Ri(e.payload.flow),r=new Map;for(let[o,s]of n){let i=t.get(o);if(!i){r.set(o,new Set(["*"]));continue}let d=fu(i,s);d.length>0&&r.set(o,new Set(d))}for(let[o]of t)n.has(o)||r.set(o,new Set(["*"]));return r}function mu(e,t){let n=[],r=[];for(let[o,s]of e){let i=t.get(o);if(!i)continue;n.push(o);let d=[];for(let c of s){if(c==="*"){d.push(...[...i].filter(u=>u!=="*")),i.has("*")&&d.push("*");continue}(i.has(c)||i.has("*"))&&d.push(c)}let l=[...new Set(d)].sort();l.length>0&&r.push({nodeId:o,fields:l})}return n.sort(),{nodeIds:n,fields:r}}function V(e,t,n={}){let r=0;for(let o of e.list())if(o.status==="pending"&&!(n.flowId&&o.payload.flowId!==n.flowId)&&o.expectedRev!==t)try{e.markClosed(o.id,"expired",hu),r+=1}catch{}return r}function Pi(e,t,n,r){let o=`Superseded by approved proposal ${n} (resultRev ${r}). Re-read the flow and resubmit if still needed.`,s=0;for(let i of e.list())if(!(i.id===n||i.status!=="pending"||i.payload.flowId!==t))try{e.markClosed(i.id,"superseded",o),s+=1}catch{}return s}function Oe(e,t,n){let r=_i(t),o=[];for(let s of e.list()){if(s.id===t.id||s.status!=="pending"||s.payload.flowId!==t.payload.flowId)continue;let i=Jo(s),d=n?gt(s.createdBy)===n:gt(s.createdBy)===gt(t.createdBy),l=mu(r,_i(s)),c=s.expectedRev===t.expectedRev,u=l.nodeIds.length>0;if(!c&&!u)continue;let p={id:s.id,title:i,displayName:Ko(i,s.createdAt),mine:d};l.nodeIds.length>0&&(p.overlappingNodeIds=l.nodeIds),l.fields.length>0&&(p.overlappingFields=l.fields),o.push(p)}return{conflictingProposals:o}}function $e(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 Z(e){return e>0?{autoClosedCount:e}:{}}var pu,hu,tt=U(()=>{"use strict";Ze();Re();pu=new Set(["x","y","w","h","g","id","z"]);a(Ri,"collectNodes");a(fu,"changedFieldKeys");a(_i,"computeProposalTouches");a(mu,"overlapBetween");hu="Proposal expired: the live flow revision moved past expectedRev. Re-read the flow and resubmit.";a(V,"expireStalePendingProposals");a(Pi,"supersedePendingOnFlow");a(Oe,"findConflictingProposals");a($e,"conflictWarningFields");a(Z,"autoClosedFields")});function Nr(e){let t=e.join("\0"),n=Cr.get(t);if(n)return n;let r=[...new Set(e.map(d=>sr(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=sr(d),c=l[l.length-1];if(!c||gu.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"),Cr.size>32&&Cr.clear(),Cr.set(t,s),s}function Kt(){return qs()?.getEffective()?.secretNamePatterns??ct}function Dn(e){let t=e.trim();return!(!t||t===te||yu.has(t.toLowerCase())||/^\$\{[A-Za-z0-9_]+\}$/.test(t))}function bu(e){let t=Li.get(e);return t||(t=vu(e),Li.set(e,t)),t}function vu(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!==te,"accept")},{hints:["?","&"],re:/[?&](?<k>[A-Za-z_][\w.-]{0,63})=(?<s>[^&#\s"'`<>]{1,2048})/dg,accept:a((t,n)=>e.matches(n.groups?.k??"")&&Dn(Tu(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:Dn(t)},"accept")}]}function Au(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 Su(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||Au(e)>=3}function xu(e){let t=e.patterns.filter(r=>ku.includes(r.toUpperCase())),n=Nr(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??"")&&Dn(r)&&!r.startsWith("$")&&!r.startsWith("{{")&&(n.matches(o.groups?.k??"")||Su(r))&&!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/.test(r)&&o.input.charAt(o.indices?.groups?.s?.[1]??0)!=="(","accept")}}function Tu(e){try{return decodeURIComponent(e)}catch{return e}}function Ru(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 _u(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=Ru(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)||Dn(e.slice(l.vs,l.ve))&&n.push({start:l.vs,end:l.ve});r=o+1}return n}function Pu(e,t,n={}){if(e.length===0)return{text:e,secrets:[]};let r=[],o=bu(t);if(!n.code){let c=Ii.get(t);c||(c=xu(t),Ii.set(t,c)),o=[...o,c]}r.push(..._u(e,t));for(let c of o){if(c.hints&&!c.hints.some(p=>e.includes(p)))continue;c.re.lastIndex=0;let u;for(;(u=c.re.exec(e))!==null;){let p=u.indices?.groups?.s,f=u.groups?.s;p&&f!==void 0&&f.length>0&&(!c.accept||c.accept(f,u))&&r.push({start:p[0],end:p[1]}),u[0].length===0&&c.re.lastIndex++}}if(r.length===0)return{text:e,secrets:[]};r.sort((c,u)=>c.start-u.start||u.end-c.end);let s=[];for(let c of r){let u=s[s.length-1];u&&c.start<u.end?u.end=Math.max(u.end,c.end):s.push({...c})}let i="",d=0,l=[];for(let c of s){let u=e.slice(c.start,c.end);u!==te&&(i+=e.slice(d,c.start)+te,l.push(u),d=c.end)}return i+=e.slice(d),{text:i,secrets:l}}function re(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function Eu(e){return re(e)&&typeof e.id=="string"&&typeof e.type=="string"}function Ui(e,t){let n=new Yo(t,e.id),r=e[$n];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!==$n&&(Mi.has(s)?o[s]=i:Hi.has(s)&&Array.isArray(i)?o[s]=i.map(d=>Zo(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[$n]=n.paths),o}function Zo(e,t){return Array.isArray(e)?e.map(n=>Zo(n,t)):Eu(e)?Ui(e,t):e}function Ir(e,t=Kt()){let n={matcher:Nr(t),count:0};return{value:Zo(e,n),count:n.count}}function Jt(e,t=Kt()){let n={...e,payload:{...e.payload,flow:Ir(e.payload.flow,t).value}};return e.baseFlow!==void 0&&(n.baseFlow=Ir(e.baseFlow,t).value),n}function bt(e,t=Kt){if(Bi(e))return e;let n=t===Kt,r=n?Ni.get(e):void 0;if(r)return r;let o={kind:e.kind,[Qo]:e,async getFlows(){let s=await e.getFlows();return{rev:s.rev,flows:Ir(s.flows,t()).value}},async getFlow(s){let i=await e.getFlow(s);return{rev:i.rev,flow:Ir(i.flow,t()).value}},updateFlow:a(s=>e.updateFlow(s),"updateFlow")};return n&&Ni.set(e,o),o}function Bi(e){return Qo in e}function qi(e){return Bi(e)?e[Qo]:e}function Me(e,t){return e.accessLink||e.auth?bt(t):t}function Fr(e){let t=0,n=e.indexOf(te);for(;n!==-1;)t++,n=e.indexOf(te,n+te.length);return t}function Fi(e){let t=e.split(te),n=[];for(let r=1;r<t.length;r++){let o=t[r-1]??"",s=t[r]??"",i=o.lastIndexOf(`
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 Cu(e,t,n,r){if(e===t)return{text:n,restored:Fr(e)};let o=Fi(e),s=Fi(t);if(o.length>s.length)return{error:`contains ${o.length} [REDACTED] placeholders but the original had ${s.length}`};let i=new Set,d=e.split(te),l=d[0]??"";for(let c=0;c<o.length;c++){let u=o[c];if(!u)break;let p=s.findIndex((f,m)=>!i.has(m)&&f.before===u.before&&f.after===u.after);if(p<0)return{error:"a [REDACTED] placeholder is not on an unchanged original line"};i.add(p),l+=(r[p]??te)+(d[c+1]??"")}return{text:l,restored:o.length}}function Oi(e){if(re(e))for(let t of Lu){let n=e[t];if(typeof n=="string"&&n.length>0)return`${t}=${n}`}}function Lr(e,t,n,r,o,s){if(typeof e=="string"){if(!e.includes(te))return e;let i=s.secrets.get(`${r}\0${o}`);if(typeof t=="string"&&typeof n=="string"&&i?.length){let d=Cu(e,t,n,i);return"error"in d?(s.unresolved.push({nodeId:r,property:o,reason:d.error}),e):(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=re(c)&&c.redacted===!0?Xo(c,"redacted"):c,p=Oi(u),f=-1;if(p!==void 0){let m=d.map((h,g)=>Oi(h)===p&&!l.has(g)?g:-1).filter(h=>h>=0);m.length>0&&(f=m[0]??-1)}return f>=0?(l.add(f),Lr(u,i[f],d[f],r,`${o}[${f}]`,s)):Lr(u,void 0,void 0,r,`${o}[?]`,s)})}if(re(e)){let i=re(t)?t:{},d=re(n)?n:{},l={};for(let[c,u]of Object.entries(e))l[c]=Lr(u,i[c],d[c],r,o?`${o}.${c}`:c,s);return l}return e}function Xo(e,t){let n={...e};return delete n[t],n}function Iu(e){let t=Xo(e,$n);return Array.isArray(t.env)&&(t.env=t.env.map(n=>!re(n)||n.redacted!==!0?n:Xo(n,"redacted"))),t}function zi(e,t,n,r){let o=typeof e.id=="string"?e.id:"",s=o?t.get(o):void 0,i={},d=s?Ui({...s,id:o},r):void 0;for(let[l,c]of Object.entries(e))if(l!==$n){if(Hi.has(l)&&Array.isArray(c)){i[l]=c.map(u=>re(u)?zi(u,t,n,r):u);continue}if(Mi.has(l)){i[l]=c;continue}i[l]=Lr(c,d?.[l],s?.[l],o,l,n)}return Iu(i)}function Wi(e,t,n=Kt()){let r=new Map;for(let l of t)re(l)&&typeof l.id=="string"&&r.set(l.id,l);let o=new Map,s={matcher:Nr(n),count:0,secrets:o},i={secrets:o,restored:0,restoredFields:[],unresolved:[]};return{flow:zi(e,r,i,s),restored:i.restored,restoredFields:i.restoredFields,unresolved:i.unresolved}}function $u(e){return typeof e.type=="string"&&Fu.has(e.type)?!0:Ou.some(t=>typeof e[t]=="string"&&e[t]!=="")}function $i(e){let t=new Set,n=a((r,o)=>{if(!(o>12)){if(typeof r=="string"){if(r.length>ji)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(re(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"&&Mu.has(r[c]))&&typeof i=="string"&&t.add(i),n(i,o+1)}},"walk");return n(e,0),t.delete(""),t}function Mn(e,t){return JSON.stringify(e)===JSON.stringify(t)}function Gi(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]);for(let r of n)if(!(Du.has(r)||r==="nodes"||r==="configs")&&!Mn(e[r],t[r]))return!0;return!1}function ju(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 Hu(e,t){return e.filter(n=>n.nodeId===t).map(n=>n.property).join(", ")}function Uu(e,t,n,r){let o=[],s=Nu.filter(d=>!Mn(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=[...Di].filter(d=>!Mn(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(",")}),Mn(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 Bu(e,t,n,r,o,s){let i=[];for(let d of ju(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?!Mn(l?.env,u?.env):l!==void 0&&u!==void 0&&Gi(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 qu(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&&!Gi(c,l))continue;e.includes(d)||i.push(d);let u=typeof l.type=="string"?l.type:"node";$u(l)&&!e.includes(d)&&s.push({code:"outbound_node_changed",error:`SECURITY: this proposal ${c?"changes":"adds"} outbound-capable node ${d} (${u}). Check where it sends data before approving.`,nodeId:d,property:"type"});let p=c?$i(c):new Set,f=[...$i(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 zu(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)re(s)&&typeof s.id=="string"&&n.set(s.id,s)}return n}function Ki(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)re(u)&&typeof u.id=="string"&&i.set(u.id,u);let d=zu(t,s),l=[],c=new Set;for(let u of o){let p=i.get(u),f=d.get(u);!p||!f||(l.push(...Uu(u,Hu(e,u),p,f)),l.push(...Bu(u,p,i,d,s,c)))}return l.push(...qu(o,i,d,s,Nr(r))),l}var $n,gu,yu,Vo,Mi,wu,Di,ji,Hi,Ei,Ci,Cr,Li,ku,Ii,Yo,Qo,Ni,Lu,Nu,Fu,Ou,Mu,Du,nt=U(()=>{"use strict";Sn();ue();Sn();$n="_aiRedactions",gu=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"]),yu=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"]),Vo=new Set(["env","msg","flow","global","jsonata","bool","date","node","cred"]),Mi=new Set(["id","type","z","g","x","y","w","h","wires","inputs","outputs","links","scope","icon","color","style","d","l","in","out","rev"]),wu=new Set(["name","label","info"]),Di=new Set(["func","initialize","finalize"]),ji=256*1024,Hi=new Set(["nodes","configs","subflows"]),Ei=["name","p","keyValue","key","property","k"],Ci={value:"type",v:"vt",to:"tot",valueValue:"valueType"},Cr=new Map;a(Nr,"createSecretNameMatcher");a(Kt,"currentSecretNamePatterns");a(Dn,"isSecretLikeValue");Li=new WeakMap;a(bu,"contentRules");a(vu,"buildContentRules");ku=["PASSWORD","PASSWD","PASSPHRASE","SECRET","CLIENT_SECRET","TOKEN","API_KEY","APIKEY","PRIVATE","CREDENTIAL","PWD","PW"];a(Au,"shannonEntropy");a(Su,"looksRandom");a(xu,"unquotedRule");a(Tu,"decodeURIComponentSafe");a(Ru,"parseConnSegment");a(_u,"connectionStringSpans");Ii=new WeakMap;a(Pu,"scanTextForSecrets");a(re,"isPlainObject");a(Eu,"isFlowEntity");Yo=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&&Dn(t)||t.length>ji)return this.record(n,[t]),te;let o=n.split(/[.[]/,1)[0]??"",s=Pu(t,this.ctx.matcher,{code:Di.has(o)});return s.secrets.length>0?(this.record(n,s.secrets),s.text):t}env(t,n){return t.map((r,o)=>{if(!re(r))return r;let s=typeof r.type=="string"?r.type:"";if(s==="cred"||typeof r.value!="string"||Vo.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(wu.has(r))return this.string(t,n,!1);let s=o?.[`${r}Type`],i=typeof s=="string"&&Vo.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)):re(t)?this.object(t,n):t}object(t,n){let r=!1;for(let s of Ei){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 Ci&&typeof i=="string"){let l=Ci[s],c=l?t[l]:void 0,u=typeof c=="string"&&Vo.has(c);o[s]=this.string(i,d,!u);continue}if(Ei.includes(s)&&typeof i=="string"){o[s]=this.string(i,d,!1);continue}o[s]=this.value(i,d,s,t)}return o}};a(Ui,"redactEntity");a(Zo,"redactValueInner");a(Ir,"redactFlowData");a(Jt,"redactProposalForAi");Qo=Symbol.for("ai-access.rawFlowAdapter"),Ni=new WeakMap;a(bt,"aiRedactingAdapter");a(Bi,"isRedactingAdapter");a(qi,"rawFlowAdapter");a(Me,"adapterForRequest");a(Fr,"countRedactionPlaceholders");a(Fi,"placeholderContexts");a(Cu,"restoreString");Lu=["name","keyValue","p","property","key","k"];a(Oi,"stableElementKey");a(Lr,"restoreWalk");a(Xo,"withoutKey");a(Iu,"stripMarkers");a(zi,"restoreEntity");a(Wi,"restoreRedactedPlaceholders");Nu=["type","url","host","hostname","server","broker","port","path","method","headers","endpoint","uri","protocol","proxy","tls","to","ret"],Fu=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"]),Ou=["url","host","hostname","server","broker","endpoint","uri"];a($u,"isOutboundNode");Mu=new Set(["env","global","flow"]);a($i,"referencedNames");Du=new Set(["x","y","w","h","l"]);a(Mn,"sameJson");a(Gi,"nodeChanged");a(ju,"scopeIds");a(Hu,"restoredFieldList");a(Uu,"secretNodeWarnings");a(Bu,"scopeWarnings");a(qu,"tabWarnings");a(zu,"proposedNodeMap");a(Ki,"secretChangeWarnings")});var ta={};Os(ta,{applyFlowNodeOps:()=>vt,applyReplaceTextToValue:()=>ea,buildPatchedFlowTab:()=>jn,collectSetProps:()=>es,decodeBase64UrlJson:()=>Hn,findTextOccurrences:()=>Qi,interpretPatch:()=>Un});function Zi(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function Wu(e){if(typeof e!="object"||e===null||Array.isArray(e))throw new y(`add op needs node as an object (got ${Zi(e)})`,"invalid_node")}async function jn(e,t,n){return vt(e,t,n.map(r=>({op:"set",nodeId:r.nodeId,set:r.set})))}function Qi(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 ea(e,t,n,r,o){if(Xi.has(t))throw new y(`Cannot change protected node property '${t}' via replace_text`,"protected_property");if(typeof n!="string"||n.length===0)throw new y("find must be a non-empty string","missing_find");if(typeof r!="string")throw new y("replace must be a string","invalid_replace");if(e===void 0)throw new y(`Field '${t}' was not found on the node`,"field_not_found");if(typeof e!="string")throw new y(`Field '${t}' is not a string (got ${e===null?"null":typeof e})`,"field_not_string");let s=Qi(e,n);if(s.length===0)throw new y(`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 y(`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 y(`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 y(`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 Gu(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 ${Zi(r)})`});continue}let o=r;(!o.id||typeof o.id!="string")&&t.push({code:"missing_node_id",error:"add op needs node.id"});try{Qe(o.type,"node.type")}catch(s){s instanceof y&&t.push({code:s.code,error:s.message,nodeId:o.id})}"wires"in o&&zt(o.wires,t,typeof o.id=="string"?o.id:void 0)}else n.op==="set"&&typeof n.set=="object"&&"wires"in n.set&&zt(n.set.wires,t,n.nodeId);return t}function Ku(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 vt(e,t,n,r={}){if(!t)throw new y("flow id is required","missing_flow_id");if(!n.length)throw new y("At least one node edit is required","missing_edits");let o=Gu(n);o.push(...jo([n.flatMap(h=>h.op==="add"?[h.node]:[])],"the batch (add ops)")),qe(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 yi(r.nodeTypes,h.node.type),Vi(c,t,h)}catch(g){if(g instanceof y)u.push({code:g.code,error:g.message,...g.statusCode!==400?{statusCode:g.statusCode}:{},...g.details??{}});else throw g}u.push(...Uo(c,t));let p=Ku(n),f=await Ho(c,r.nodeTypes,{softWarningNodeIds:p});u.push(...f.errors),qe(u);for(let h of n)Vi(i,t,h);let m=[...f.warnings];if(r.nodeTypes){let h=await Bo(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 Ji(e,t,n){if(!t||typeof t!="string")throw new y("Each edit needs a node id","missing_node_id");let r=e.findIndex(o=>o.id===t);if(r<0||!e[r])throw new y(`Node '${t}' was not found in flow '${n}'`,"node_not_found",{statusCode:404});return r}function Vi(e,t,n){if(n.op==="add"){let i=n.node;if(!i.id||typeof i.id!="string")throw new y("add op needs node.id","missing_node_id");if(Qe(i.type,"node.type"),e.some(d=>d.id===i.id))throw new y(`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=Ji(e,n.nodeId,t);e.splice(i,1);return}let r=Ji(e,n.nodeId,t),o=e[r];if(!o)throw new y(`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 y(`Field '${n.field}' was not found on the node`,"field_not_found");let i=ea(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(Xi.has(i))throw new y(`Cannot change protected node property '${i}' via GET propose`,"protected_property");if(i==="wires"){let l=[];zt(d,l,n.nodeId),qe(l)}s[i]=d}e[r]=s}function Yi(e){if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean"||typeof e=="bigint")return String(e)}function es(e){let t={},n=e.set;if(n&&typeof n=="object"&&!Array.isArray(n))for(let[r,o]of Object.entries(n)){let s=Yi(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=Yi(o);i!==void 0&&(t[s]=i)}}return t}function Hn(e){let t;try{t=Buffer.from(e,"base64url").toString("utf8")}catch{throw new y("patch must be base64url-encoded JSON","invalid_patch")}try{return JSON.parse(t)}catch{throw new y("patch JSON is malformed","invalid_patch_json")}}function Ju(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 ts(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 Vu(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 y("occurrence must be a positive integer","invalid_occurrence");if(!Number.isFinite(t))throw new y("occurrence must be a positive integer","invalid_occurrence");return Math.trunc(t)}function Yu(e){let t=e.node;if(t!==void 0)return Wu(t),{op:"add",node:t};let n={...e};return delete n.op,{op:"add",node:n}}function Xu(e){let t=ts(e);if(!t)throw new y("replace_text op needs node id","missing_node_id");let n=typeof e.field=="string"?e.field:"";if(!n)throw new y("replace_text op needs field","missing_field");if(typeof e.find!="string")throw new y("replace_text op needs find string","missing_find");if(typeof e.replace!="string")throw new y("replace_text op needs replace string","invalid_replace");let r=Vu(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 Zu(e){let t=ts(e);if(!t)throw new y("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 Qu(e){let t=typeof e.op=="string"?e.op.toLowerCase():"set";if(t==="add")return Yu(e);if(t==="remove"){let n=ts(e);if(!n)throw new y("remove op needs id","missing_node_id");return{op:"remove",nodeId:n}}if(t==="replace_text")return Xu(e);if(t!=="set")throw new y("nodes[].op must be 'add', 'remove', 'set', or 'replace_text'","invalid_patch");return Zu(e)}function Un(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new y("patch must be a JSON object","invalid_patch");let n=e;if(n.type==="replace_flow"||n.flow&&typeof n.flow=="object"){let r=Ju(n,t);if(!r)throw new y("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 y("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 y("Each patch.nodes entry must be an object","invalid_patch");o.push(Qu(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 y("patch must include nodes[] edits or type/flow for replace_flow","invalid_patch")}var Xi,Or=U(()=>{"use strict";J();qo();wt();Xi=new Set(["id","type","z"]);a(Zi,"describeNonObjectNode");a(Wu,"assertAddNodeObject");a(jn,"buildPatchedFlowTab");a(Qi,"findTextOccurrences");a(ea,"applyReplaceTextToValue");a(Gu,"collectOpShapeIssues");a(Ku,"touchedNodeIdsFromOps");a(vt,"applyFlowNodeOps");a(Ji,"requireNodeIndex");a(Vi,"applyOneNodeOp");a(Yi,"stringifyQueryValue");a(es,"collectSetProps");a(Hn,"decodeBase64UrlJson");a(Ju,"resolveReplaceFlowId");a(ts,"resolveNodeId");a(Vu,"parseOccurrence");a(Yu,"parseAddOp");a(Xu,"parseReplaceTextOp");a(Zu,"parseSetOp");a(Qu,"parseNodeOp");a(Un,"interpretPatch")});function Vt(e,t){return gt(e.createdBy)===t.id}function ns(e,t){return!t||t.length===0||t.includes(e)}function ep(e){return typeof e=="string"&&ra.has(e)}function Xt(e,t){let n=tp[e],r=e!=="status"&&Array.isArray(t);return new y(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 $r(e,t){if(Array.isArray(t))throw Xt(e,t);return t}function np(e){return typeof e=="number"?Number.isInteger(e)?e:null:typeof e=="string"&&/^-?\d+$/.test(e.trim())?Number(e.trim()):null}function rs(e){return e==null||e===""}function rp(e){if(!rs(e)){if(e==="true"||e==="1"||e===!0)return!0;if(e==="false"||e==="0"||e===!1)return!1;throw Xt("mine",e)}}function os(e){if(rs(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(!ep(o))throw Xt("status",e);n.includes(o)||n.push(o)}}return n.length>0?n:void 0}function na(e,t,n,r){if(rs(t))return;let o=np(t);if(o===null||o<n||o>r)throw Xt(e,t);return o}function Mr(e){let t={},n=rp($r("mine",e.mine));n!==void 0&&(t.mine=n);let r=os(e.status);r&&(t.status=r);let o=$r("flowId",e.flowId??e.flow_id??e.flow);typeof o=="string"&&o.length>0&&(t.flowId=o);let s=na("limit",$r("limit",e.limit),1,Yt);s!==void 0&&(t.limit=s);let i=na("offset",$r("offset",e.offset),0,Number.MAX_SAFE_INTEGER);return i!==void 0&&(t.offset=i),t}function kt(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 op(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),Yt);return{items:e.slice(r,r+o),total:n,limit:o,offset:r}}function Zt(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||!Vt(c,t)).filter(c=>ns(c.status,r.status)).map(c=>({...ae(Jt(c),n),mine:Vt(c,t)})),l=op(d,r);return{proposals:l.items,total:l.total,limit:l.limit,offset:l.offset}}function ze(e,t,n,r={}){if(!n)throw new y("Missing proposal id","missing_proposal_id");let o=e.get(n);return{...ae(Jt(o),r),mine:Vt(o,t)}}function sp(e){let t={kind:"access-link",linkId:e.id};return typeof e.label=="string"&&e.label&&(t.label=e.label),t}function Qt(e,t,n,r,o,s={}){if(!r)throw new y("Missing proposal id","missing_proposal_id");let i;try{i=e.get(r)}catch(f){throw f instanceof se,f}if(!Vt(i,n))throw new Bt(r);if(i.status!=="pending")throw new ne(r,i.status);let d=typeof o=="string"&&o.trim()?o.replace(/\0/g,"").trim().slice(0,2e3):"Withdrawn by access link",l=sp(n),c=e.markWithdrawn(r,l,d),u=ae(Jt(c),s),p=u.flowLabel||void 0;return t.append("proposal.withdrawn",l,{proposalId:c.id,flowId:c.payload.flowId,title:u.title,displayName:u.displayName,reason:d,...p?{flowLabel:p}:{}}),{...u,mine:!0}}function ip(e,t,n,r={}){let o;try{o=e.get(t.id)}catch{return{}}if(r.liveRev){V(e,r.liveRev,{flowId:o.payload.flowId});try{o=e.get(t.id)}catch{return{}}}return o.status!=="pending"?{}:$e(Oe(e,o,n))}function ge(e,t,n,r={},o={}){let s="mine"in n?n:{...ae(n,r),mine:Vt(n,t)},i=0;if(o.liveRev)try{let c=e.get(s.id).payload.flowId;i=V(e,o.liveRev,{flowId:c});let u=e.get(s.id);s={...ae(u,r),mine:Vt(u,t)}}catch{}let d=he(Jt(s),r),l={...d,mine:s.mine,...Z(i)};return o.includeConflicts!==!1&&d.status==="pending"&&Object.assign(l,ip(e,d,t.id)),l}async function ye(e){let t=await e.getFlows();return{rev:t.rev,flows:t.flows}}async function _e(e,t){if(!t)throw new y("Missing flow id","missing_flow_id");let n=await e.getFlow(t);return{rev:n.rev,flow:qt(t,n.flow)}}function ap(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 oa(e,t){if("nodes"in e&&e.nodes!==void 0&&!Array.isArray(e.nodes))throw new y("payload.nodes must be an array of node ops","invalid_nodes");if(t!==void 0&&!Array.isArray(t))throw new y("nodes must be an array of node ops","invalid_nodes")}async function dp(e,t,n,r,o){let{applyFlowNodeOps:s,interpretPatch:i}=await Promise.resolve().then(()=>(Or(),ta));oa(n,r);let d=Array.isArray(n.nodes)?n.nodes:Array.isArray(r)?r:null;if(!d)throw new y("payload.flow is required","missing_flow");let l=n.flowId,c=typeof l=="string"?l:"";if(!c)throw new y("payload.flowId is required","missing_flow_id");let u=i({nodes:d,flowId:c},c);if(u.kind!=="nodes")throw new y("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 K(t,p.expectedRev,"submit_proposal");return{flowId:u.flowId,flow:p.flow}}async function lp(e,t,n,r,o){let s=n.flowId,i=n.flow,d=!i||typeof i!="object"||Array.isArray(i);if(d&&oa(n,r),d&&(Array.isArray(n.nodes)||Array.isArray(r))){let l=await dp(e,t,n,r,o);s=l.flowId,i=l.flow}if(typeof s!="string"||s.length===0)throw new y("payload.flowId is required","missing_flow_id");if(!i||typeof i!="object"||Array.isArray(i))throw new y("payload.flow is required","missing_flow");return{flowId:s,flow:i}}function cp(e,t){try{return{safeExpectedRev:Fe(e.slice(0,200),"expectedRev"),safeFlowId:Fe(t.slice(0,200),"flowId")}}catch{throw new y("expectedRev and payload.flowId must be safe path segments","invalid_path_segment")}}function up(e,t){let n=Wi(ht(e),t);return n.unresolved.length>0&&qe(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 rt(e,t,n,r,o,s={},i){let d=r.type,l=ap(r),c=r.payload;if(d!=="replace_flow")throw new y("type must be 'replace_flow'","invalid_proposal_type");if(!l)throw new y("expectedRev is required (top-level field next to type and payload)","missing_expected_rev");if(!c||typeof c!="object"||Array.isArray(c))throw new y("payload must be an object","invalid_payload");let u=c,p=qi(o),f=await lp(bt(p),l,u,r.nodes,i),{safeExpectedRev:m,safeFlowId:h}=cp(l,f.flowId),g=f.flow,k=Buffer.byteLength(JSON.stringify(c),"utf8");if(k>H)throw new Y("proposal payload",k,H);qe(vi(g,h));let C=await p.getFlows();if(C.rev!==m)throw new K(m,C.rev,"submit_proposal");let L=up(g,C.flows),N=ht(L.flow);qe(bi(N,h,C.flows));let Ue=await p.getFlow(h);if(Ue.rev!==m)throw new K(m,Ue.rev,"submit_proposal");let Ce=ht(Ue.flow);i&&await gi(i,N);let{errors:S,warnings:b}=await ki(N,{flowId:h,baseFlow:Ce,...i?{nodeTypes:i}:{}});qe(S),b.unshift(...Ki(L.restoredFields,N,C.flows).map(le=>({...le})));let w={kind:"access-link",linkId:n.id};n.label!==void 0&&(w.label=n.label);let v=n.oauthClientId&&n.oauthUser?{kind:"oauth",clientId:n.oauthClientId,user:n.oauthUser}:w,A=Er(r.title)??Go(Ce,N),E=e.create({type:"replace_flow",expectedRev:m,payload:{flowId:h,flow:N},baseFlow:Ce,...L.restoredFields.length>0?{restoredSecrets:L.restoredFields}:{},title:A,createdBy:v,...b.length>0?{warnings:b}:{}}),$=ae(Jt(E),s),F=$.flowLabel||void 0;return t.append("proposal.created",w,{proposalId:E.id,flowId:h,expectedRev:m,title:$.title,displayName:$.displayName,...F?{flowLabel:F}:{},...b.length>0?{warningCount:b.length}:{},...L.restored>0?{restoredRedactions:L.restored}:{}}),$}function M(e,t="rest"){return Bn(e,t)}var Yt,ra,tp,y,J=U(()=>{"use strict";xe();Ar();Ze();yr();G();De();qo();wt();tt();Re();nt();a(Vt,"isMineProposal");Yt=200,ra=new Set(["pending","approved","rejected","failed","withdrawn","expired","superseded"]);a(ns,"matchesStatusFilter");a(ep,"isProposalStatus");tp={status:[...ra].join(", "),limit:`integer 1\u2013${Yt}`,offset:"integer \u2265 0",mine:"true, false, 1, 0",flowId:"one flow tab id"};a(Xt,"invalidFilterError");a($r,"singleQueryValue");a(np,"parseIntegerFilter");a(rs,"isUnset");a(rp,"parseMineFilter");a(os,"parseStatusFilter");a(na,"parseRangeFilter");a(Mr,"parseListProposalsQuery");a(kt,"listPageFields");a(op,"applyListPagination");a(Zt,"listAccessibleProposals");a(ze,"getAccessibleProposal");a(sp,"accessLinkActor");a(Qt,"withdrawMyProposal");a(ip,"conflictsForProposal");a(ge,"presentAccessibleProposalForApi");y=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(ye,"listFlows");a(_e,"getFlow");a(ap,"resolveExpectedRev");a(oa,"assertNodesArrayOrAbsent");a(dp,"materializePayloadFromNodes");a(lp,"resolveReplaceFlowPayload");a(cp,"assertSafeRevAndFlowId");a(up,"strictRestoreOrThrow");a(rt,"submitReplaceFlowProposal");a(M,"aiOpsErrorToHttp")});function ss(e){return e==="get_link"?86400:2592e3}function is(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 At,qn=U(()=>{"use strict";a(ss,"maxTtlSecondsForKind");a(is,"ttlCapErrorMessage");At="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 x(e,t,n,r,o=oe,s="error"){return{code:e,status:t,meaning:n,hint:r,channels:o,kind:s}}function zn(e){return fp.get(e)?.hint}function sa(e){return e.replace(/\\/g,"\\\\").replace(/\|/g,"\\|")}function ia(){let e=a(s=>`| \`${s.code}\` | ${s.status} | ${sa(s.meaning)} | ${sa(s.hint)} | ${s.channels.join(", ")} |`,"row"),t=["| Code | HTTP | Meaning | Next step | Channels |","| --- | --- | --- | --- | --- |"],n=Dr.filter(s=>s.kind==="error"),r=Dr.filter(s=>s.kind==="warning"),o=Dr.filter(s=>s.kind==="status");return[...t,...n.map(e),"","**Warnings** (in `warnings[]` on a successful propose; never blocking):","",...t,...r.map(e),"","**Auto-closed proposal statuses.** Pending proposals are closed automatically: `expired` when the live flow rev moves past their `expectedRev`, `superseded` when another proposal on the same flow is approved. Submit, get, and list responses include `autoClosedCount` when that request auto-closed proposals (omitted when zero).","",...t,...o.map(e)].join(`
10
+ `)}var oe,en,as,pp,tn,Dr,fp,jr=U(()=>{"use strict";qn();oe=["GET","REST","MCP"],en=["GET"],as=["REST","MCP"],pp=["GET","REST"],tn=["admin"];a(x,"e");Dr=[x("access_link_missing",401,"No Bearer / path token","Ask the user for a new access link of the right kind"),x("bearer_scheme_required",401,"Auth used Basic (or non-Bearer)","Send `Authorization: Bearer <token>`",as),x("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`)"),x("access_link_invalid",401,"Unknown, revoked, or cleared after restart","Ask the user for a new access link"),x("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"),x("ai_features_disabled",403,"Plugin AI features off","Owner enables AI Access / `adminAuth`"),x("auth_required",401,"Admin session required","Sign in to the Node-RED editor",tn),x("insufficient_permissions",403,"Admin lacks required scope","Need `*` for link mint / approve",tn),x("invalid_link_kind",400,"Unknown `kind` on create","Use `get_link`, `rest`, or `mcp`",tn),x("invalid_ttl",400,"`ttlSeconds` is zero, negative, not a number, or over the cap for the kind",At,tn),x("access_link_limit",409,"Too many active links","Revoke an existing link first",tn),x("access_link_not_found",404,"Unknown link id on revoke","List links and use an existing id",tn),x("link_create_not_supported",403,"Attempted mint under a GET link","Admin mints via `POST /mcp/api/v1/links` only",en),x("rate_limited",429,"Per-IP or per-token limit","Wait `retryAfter` / `Retry-After`"),x("payload_too_large",413,"Body / proposal over ~1.5MB","Shrink content"),x("invalid_json",400,"Malformed JSON body","Fix Content-Type / body",as),x("unsupported_media_type",415,"POST body is not `application/json`","Send `Content-Type: application/json`",as),x("url_too_long",414,"GET URL over soft cap (~8KB)","Use chunked drafts or REST/MCP",en),x("not_found",404,"Unknown path or resource","Re-read docs / discovery"),x("missing_query",400,"Required search `q` missing","Pass `?q=`",pp),x("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"),x("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"]),x("invalid_diff_format",400,"Unknown `format` on proposal diff","Use `format=summary` (default) or `format=full`"),x("confirm_required",400,"GET withdraw without `confirm=1`","Repeat the request with `confirm=1`",en),x("missing_expected_rev",400,"Proposal submitted without `expectedRev` / `rev`","Read the flow and pass its `rev`"),x("version_mismatch",409,"Stale `rev` / `expectedRev`","Re-read flows and resubmit"),x("proposal_not_found",404,"Unknown proposal id","List proposals and use an existing id"),x("proposal_not_pending",409,"Action needs a pending proposal","Only pending can withdraw / decide"),x("proposal_not_owned",403,"Withdraw of another token\u2019s proposal","Only the creating token can withdraw"),x("validation_failed",400,"Several validation errors at once (message `N validation errors`)","Fix every entry in errors[] and resubmit."),x("duplicate_node_id",400,"The same node id appears more than once in a full tab or batch","Give every node a unique id"),x("missing_node_id",400,"Node / op without an id","Include a non-empty string id"),x("missing_node_type",400,"Node without a `type`","Set `type` to an installed palette type name"),x("invalid_node_type",400,"`type` is not a string","Use the palette type name exactly"),x("invalid_nodes",400,"`flow.nodes` / `flow.configs` not an array","Send arrays (use `[]` to clear)"),x("invalid_node",400,"Node entry is not an object","Send `{ id, type, \u2026 }` objects"),x("invalid_wires",400,"Bad wires shape, missing target, or more ports than outputs","Wire only to existing node ids / ports"),x("z_mismatch",400,"Node `z` differs from the flow id","Set `z` to the tab id or omit it"),x("node_already_exists",400,"`add` op for an id already in the flow","Use `set` or a new id"),x("protected_property",400,"Tried to change id / type / z","Omit id, type, and z from edits"),x("node_not_found",404,"Unknown node (single error only; several errors \u2192 `validation_failed`)","Re-read the flow"),x("field_not_found",404,"Unknown field on a node","Re-read the node"),x("text_not_found",400,"`replace_text` find had no match","Re-read field; find is exact"),x("ambiguous_match",409,"`replace_text` matched more than once","Pass `occurrence` (1-based) or unique find"),x("unknown_node_type",400,"Proposal uses a type that is not installed","List node-types / palette search; ask user to install"),x("node_type_not_found",404,"Unknown type on node-types detail","List types or search palette"),x("chunk_conflict",409,"Same draft seq re-sent with different data","Re-send identical data or start a new draft",en),x("missing_chunks",400,"Draft submit with gaps in seq","Upload every seq from 0 through max",en),x("draft_hash_mismatch",400,"Draft sha256 does not match assembled bytes","Send the hex sha256 of the assembled UTF-8",en),x("missing_config_node",201,"Warning: node has no (or an unresolved) config node yet","Configure before deploy if needed; see `candidates`",oe,"warning"),x("unwired_node",201,"Warning: node has outputs but no wires","Wire it, or ignore while scaffolding",oe,"warning"),x("incomplete_wires",201,"Warning: fewer wire ports than outputs","Add ports if outputs should connect",oe,"warning"),x("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",oe),x("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",oe,"warning"),x("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",oe,"warning"),x("secret_wires_changed",201,"Warning: a node holding a restored secret had its output wires changed","Reviewer must check where its messages now go",oe,"warning"),x("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}`)",oe,"warning"),x("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",oe,"warning"),x("secret_tab_changed",201,"Warning: proposal with restored secrets also changes other nodes in the tab","Review every change before approving",oe,"warning"),x("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",oe,"warning"),x("expired",200,"Status: pending proposal auto-closed because the live flow rev moved past `expectedRev`","Re-read the flow and submit a new proposal",oe,"status"),x("superseded",200,"Status: pending proposal auto-closed because another proposal on the same flow was approved","Re-read the flow and submit a new proposal if still needed",oe,"status"),x("upstream_error",502,"Node-RED runtime/flows error","Retry; check Node-RED logs"),x("internal_error",500,"Unexpected server error","Retry; check Node-RED logs")],fp=new Map(Dr.map(e=>[e.code,e]));a(zn,"registryHint");a(sa,"cell");a(ia,"renderGetLinkErrorTable")});function de(e,t,n={}){return ds(e,t,n)}function mp(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 hp(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 ot(e,t,n,r){return e==="mcp"?t:e==="rest"?n:r}function ds(e,t,n={}){switch(e){case"version_mismatch":return ot(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 ot(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 ot(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 ot(t,"Call list_proposals and use an id from that response.","List proposals and use an id from that response.","List proposals and use an id from that response.");case"proposal_not_owned":return"Only the access link that created a pending proposal can withdraw it.";case"proposal_not_pending":return"Only pending proposals can be withdrawn or decided.";case"confirm_required":return"Add confirm=1 to the withdraw URL (required so link prefetchers cannot withdraw).";case"invalid_wires":return mp(t,n.cause);case"node_type_not_found":case"unknown_node_type":return hp(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 ot(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 ot(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 ot(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 ot(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 Bn(e,t,n={}){let r=n.canonicalizeMessage!==!1,o=null;if(e instanceof y?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,...e.details??{}}}:e instanceof K?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,expectedRev:e.expectedRev,currentRev:e.currentRev}}:e instanceof W?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,flowId:e.flowId}}:e instanceof se||e instanceof Bt||e instanceof ne||e instanceof Y?o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}:(e instanceof Ne||e instanceof Se)&&(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=je[s];i&&(o.body.error=i)}if(typeof o.body.hint!="string"||!o.body.hint){let i=ds(s,t,{cause:o.body.cause})??zn(s);i&&(o.body.hint=i)}return o}function aa(e,t,n={}){let o={error:n.error??je[t]??"Request failed",code:t,...n.details??{}},s=ds(t,e,{cause:o.cause})??zn(t);return s&&(o.hint=s),{statusCode:n.statusCode??400,body:o}}var je,De=U(()=>{"use strict";xe();Ze();G();J();jr();je={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(de,"channelErrorHint");a(mp,"invalidWiresHint");a(hp,"nodeTypeHint");a(ot,"pickChannel");a(ds,"hintFor");a(Bn,"mapErrorForChannel");a(aa,"buildChannelError")});var tg={};Os(tg,{ACCESS_LINK_INVALID_MESSAGE:()=>ut,ACCESS_LINK_MISSING_MESSAGE:()=>me,AccessLinkStore:()=>Ct,EDITOR_SIDEBAR_ASSET_VERSION:()=>on,MAX_PROPOSAL_JSON_BYTES:()=>H,SettingsStore:()=>Mt,acceptedMediaTypes:()=>wl,areAiFeaturesEnabled:()=>ke,bindSettingsStore:()=>cr,createPluginServices:()=>bo,default:()=>eg,getStorageDir:()=>co,isAdminAuthEnabled:()=>z,isMcpStreamRequest:()=>bl,packageVersion:()=>rn,registerRoutes:()=>vl,renderEditorSidebarJs:()=>Hr,renderEditorSidebarLoaderHtml:()=>ua,requireAccessLink:()=>Ut,resolveAiAccessSettings:()=>ve});module.exports=Rl(tg);var yl=require("fs");var tr=require("fs"),bn=require("path");G();function nr(e){let t=(0,bn.join)(e,".ai-access");return{root:t,proposals:(0,bn.join)(t,"proposals"),logs:(0,bn.join)(t,"logs"),settings:(0,bn.join)(t,"config.json")}}a(nr,"resolveAiAccessPaths");function Ao(e){(0,tr.mkdirSync)(e,{recursive:!0,mode:ko});try{(0,tr.chmodSync)(e,ko)}catch{}}a(Ao,"mkdirSecure");function Hs(e){Ao(e.root),Ao(e.proposals),Ao(e.logs)}a(Hs,"ensureAiAccessDirs");pt();Le();function mr(e){let t=pe(e);return`auth-tokens${(t==="/"?"":t).replace(/\//g,"-")}`}a(mr,"editorAuthTokenKey");Le();De();function gp(e){if(!e)return!1;let t=e.split(";")[0]?.trim().toLowerCase()??"";return t==="application/json"||t.endsWith("+json")}a(gp,"isJsonContentType");function yp(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(yp,"hasRequestBody");function We(e="rest"){return(t,n,r)=>{let o=t.headers["content-type"];if(gp(o)){r();return}if(!o&&!yp(t)){r();return}let s=aa(e,"unsupported_media_type",{statusCode:415});n.status(s.statusCode).json(s.body)}}a(We,"requireJsonContentType");var wp="text/plain; charset=utf-8",bp="text/html; charset=utf-8",vp="default-src 'none'; base-uri 'self'; form-action 'none'; frame-ancestors 'none'";function kp(e){let t=e.query?.format;return typeof t=="string"?t.toLowerCase():void 0}a(kp,"formatParam");function Ap(e){let t=kp(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(Ap,"chooseGetLinkRepresentation");function da(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}a(da,"escapeHtml");var Sp=/(?<![A-Za-z0-9_.~\-/])\.\/[A-Za-z0-9._~\-/]+(?:\?[A-Za-z0-9._~\-=&%]+)?/g;function xp(e){return e.replace(Sp,(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(xp,"linkifyEscaped");function Tp(e,t){return`<!doctype html>
11
+ <html><head><meta charset="utf-8">${t?`<base href="${da(t)}">`:""}<title>Node-RED AI Access</title></head><body><pre>${xp(da(e))}</pre></body></html>
12
+ `}a(Tp,"renderGetLinkHtml");function Rp(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(Rp,"getLinkBaseHref");function _p(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}a(_p,"prettyJson");function la(){return(e,t,n)=>{let r=Ap(e),o=Rp(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",wp),s(u)):(t.setHeader("Content-Type",bp),t.setHeader("Content-Security-Policy",vp),s(Tp(l?_p(u):u,o)))}),n()}}a(la,"getLinkContentNegotiation");rr();Le();function Pp(e){let t=fe(e.settings.httpAdminRoot),n=Ve(e.settings.httpAdminRoot),r="0.2.0-beta.5",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(Pp,"buildDiscoveryIndex");function ls(e){return(t,n)=>{n.status(200).json(Pp(e))}}a(ls,"discoveryIndexHandler");function ca(){return(e,t)=>{t.status(404).json({error:"Not found",code:"not_found"})}}a(ca,"mcpApiNotFoundHandler");var nn;function rn(){return nn||(nn="0.2.0-beta.5",nn)}a(rn,"packageVersion");var on=rn();function Ep(){return`#red-ui-sidebar-ai-access {
10
13
  position: relative;
11
14
  height: 100%;
12
15
  display: flex;
@@ -162,7 +165,7 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
162
165
  border: 0;
163
166
  background: transparent;
164
167
  color: transparent;
165
- }`}a(Qc,"editorSidebarStyles");function Zc(){return`
168
+ }`}a(Ep,"editorSidebarStyles");function Cp(){return`
166
169
  /**
167
170
  * Editor right-hand sidebar tab: AI Access.
168
171
  * Served from /mcp/editor/ai-access-sidebar.js (no-store + ?v= cache-bust).
@@ -460,7 +463,7 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
460
463
  saving = false;
461
464
  });
462
465
  }
463
- `}a(Zc,"editorSidebarScriptPartA");function eu(){return`
466
+ `}a(Cp,"editorSidebarScriptPartA");function Lp(){return`
464
467
  function buildContent() {
465
468
  var content = $('<div></div>', { id: CONTENT_ID, class: 'red-ui-sidebar-ai-access' });
466
469
 
@@ -562,8 +565,8 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
562
565
  },
563
566
  });
564
567
  })();
565
- `}a(eu,"editorSidebarScriptPartB");function tu(){return Zc()+eu()}a(tu,"editorSidebarScriptBody");function yr(){let e=JSON.stringify(Qc());return[`/* @com55/node-red-ai-access editor sidebar v=${Xt} */`,"(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);"," }","})();",tu()].join(`
566
- `)}a(yr,"renderEditorSidebarJs");function wi(e=Xt){return`<script>
568
+ `}a(Lp,"editorSidebarScriptPartB");function Ip(){return Cp()+Lp()}a(Ip,"editorSidebarScriptBody");function Hr(){let e=JSON.stringify(Ep());return[`/* @com55/node-red-ai-access editor sidebar v=${on} */`,"(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);"," }","})();",Ip()].join(`
569
+ `)}a(Hr,"renderEditorSidebarJs");function ua(e=on){return`<script>
567
570
  /**
568
571
  * Thin loader for the AI Access editor sidebar.
569
572
  * Logic lives at GET /mcp/editor/ai-access-sidebar.js (Cache-Control: no-store)
@@ -597,8 +600,8 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
597
600
  (document.head || document.documentElement).appendChild(s);
598
601
  })();
599
602
  </script>
600
- `}a(wi,"renderEditorSidebarLoaderHtml");K();Fe();function bi(e){return Oe[e]??"Request failed"}a(bi,"safeErrorText");function nu(e,t){let n=O(t,"rest");if(!n)return!1;let r=n.body.code,o={error:bi(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(nu,"sendMappedFlowError");function vi(e){return async(t,n)=>{try{n.json(await pe(e))}catch(r){if(nu(n,r))return;n.setHeader("X-Content-Type-Options","nosniff"),n.status(502).json({error:bi("upstream_error"),code:"upstream_error"})}}}a(vi,"listFlowsHandler");ie();K();_e();Fe();function Yt(e){return e==null||e===""?{ok:!0,format:"summary"}:e==="summary"||e==="full"?{ok:!0,format:e}:{ok:!1}}a(Yt,"resolveDiffFormat");function ru(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(ru,"compactLineDiff");function ou(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=ru(e.lines),d={key:e.key,kind:"text",beforeBytes:o,afterBytes:s,lines:i.lines};return i.truncated&&(d.truncated=!0),d}a(ou,"formatTextChange");function su(e,t){return e.map(n=>n.kind==="text"?ou(n,t):n)}a(su,"formatPropertyChanges");function ki(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)}
601
- \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(ki,"formatPresenceText");function Ai(e,t){return{summary:e.summary,added:e.added.map(n=>ki(n,t)),removed:e.removed.map(n=>ki(n,t)),modified:e.modified.map(n=>({...n,changes:su(n.changes,t)}))}}a(Ai,"formatSections");function Qt(e,t="summary"){let n=Ai(e,t),r={...e,...n,format:t};return e.vsCurrent&&(r.vsCurrent=Ai(e.vsCurrent,t)),r}a(Qt,"applyDiffFormat");G();ie();gr();function Zt(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}a(Zt,"escapeForDocs");var xi="https://YOUR-NODE-RED/mcp/rpc";function iu(e,t,n,r=xi){return`## Also available (prefer when your client can send headers)
603
+ `}a(ua,"renderEditorSidebarLoaderHtml");J();De();nt();function pa(e){return je[e]??"Request failed"}a(pa,"safeErrorText");function Np(e,t){let n=M(t,"rest");if(!n)return!1;let r=n.body.code,o={error:pa(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(Np,"sendMappedFlowError");function fa(e){return async(t,n)=>{try{n.json(await ye(Me(t,e)))}catch(r){if(Np(n,r))return;n.setHeader("X-Content-Type-Options","nosniff"),n.status(502).json({error:pa("upstream_error"),code:"upstream_error"})}}}a(fa,"listFlowsHandler");ce();J();Le();De();function sn(e){return e==null||e===""?{ok:!0,format:"summary"}:e==="summary"||e==="full"?{ok:!0,format:e}:{ok:!1}}a(sn,"resolveDiffFormat");function Fp(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(Fp,"compactLineDiff");function Op(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=Fp(e.lines),d={key:e.key,kind:"text",beforeBytes:o,afterBytes:s,lines:i.lines};return i.truncated&&(d.truncated=!0),d}a(Op,"formatTextChange");function $p(e,t){return e.map(n=>n.kind==="text"?Op(n,t):n)}a($p,"formatPropertyChanges");function ma(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)}
604
+ \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(ma,"formatPresenceText");function ha(e,t){return{summary:e.summary,added:e.added.map(n=>ma(n,t)),removed:e.removed.map(n=>ma(n,t)),modified:e.modified.map(n=>({...n,changes:$p(n.changes,t)}))}}a(ha,"formatSections");function an(e,t="summary"){let n=ha(e,t),r={...e,...n,format:t};return e.vsCurrent&&(r.vsCurrent=ha(e.vsCurrent,t)),r}a(an,"applyDiffFormat");G();ce();jr();function dn(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}a(dn,"escapeForDocs");var ga="https://YOUR-NODE-RED/mcp/rpc";function Mp(e,t,n,r=ga){return`## Also available (prefer when your client can send headers)
602
605
 
603
606
  **Prefer MCP or REST** whenever the AI client can send custom headers and JSON
604
607
  bodies \u2014 no URL length limits and no chunked drafts. Use this **GET link
@@ -667,7 +670,7 @@ curl -sS -X POST "${r}" \\
667
670
  -H "Accept: application/json, text/event-stream" \\
668
671
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
669
672
  \`\`\`
670
- `}a(iu,"docsAlsoAvailable");function au(e,t){return`# Node-RED AI Access \u2014 self-contained GET link (get_link)${e}
673
+ `}a(Mp,"docsAlsoAvailable");function Dp(e,t){return`# Node-RED AI Access \u2014 self-contained GET link (get_link)${e}
671
674
 
672
675
  **Git mental model:** a proposal is like a pull request. Human approve = merge;
673
676
  reject = close; withdraw = you close your own PR. If \`rev\`/\`expectedRev\` does
@@ -742,7 +745,7 @@ fields at the top level** (no \`ok\` / \`proposal\` wrapper). Read
742
745
 
743
746
  Lean by default (no \`payload\` / \`baseFlow\`). Pass
744
747
  \`?include=payload,baseFlow\` or \`?include_payload=true\` to include them.
745
- `}a(au,"docsIntro");function du(){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; pending+stale may include `vsCurrent`.\n9. Withdraw your own pending proposal:\n `GET ./proposals/<id>/withdraw?confirm=1&reason=<text>` (status becomes `withdrawn`,\n not `rejected`). Only the link that created a proposal can withdraw it;\n others get `403 proposal_not_owned`.\n\n**Recommended edit loop:** summary \u2192 search \u2192 read a line range \u2192\n`replace_text` \u2192 propose.\n\nOn submit and on read of a pending proposal, the response may include\n`conflictingProposals` (same-rev or overlapping pending 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(du,"docsWorkflow");function lu(){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(lu,"docsUrlTable");function cu(){return`## Propose examples
748
+ `}a(Dp,"docsIntro");function jp(){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; pending+stale may include `vsCurrent`.\n9. Withdraw your own pending proposal:\n `GET ./proposals/<id>/withdraw?confirm=1&reason=<text>` (status becomes `withdrawn`,\n not `rejected`). Only the link that created a proposal can withdraw it;\n others get `403 proposal_not_owned`.\n\n**Recommended edit loop:** summary \u2192 search \u2192 read a line range \u2192\n`replace_text` \u2192 propose.\n\nOn submit and on read of a pending proposal, the response may include\n`conflictingProposals` (same-rev or overlapping pending 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(jp,"docsWorkflow");function Hp(){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(Hp,"docsUrlTable");function Up(){return`## Propose examples
746
749
 
747
750
  **Recommended for small edits to long fields** \u2014 \`replace_text\` (find must be
748
751
  **unique** unless you pass \`occurrence\` as a 1-based index). Creates a normal
@@ -820,11 +823,11 @@ GET ./propose?flow=tab1&rev=abc123&patch=<base64url>
820
823
  Node-level proposes create a normal \`replace_flow\` proposal; owners should
821
824
  approve with deployment type **\`nodes\`** (plugin default) so only changed
822
825
  nodes deploy.
823
- `}a(cu,"docsProposeExamples");function uu(){return`## Chunked drafts (long HTML / large fields)
826
+ `}a(Up,"docsProposeExamples");function Bp(){return`## Chunked drafts (long HTML / large fields)
824
827
 
825
- When the GET URL would exceed **${It} bytes**, do not
828
+ When the GET URL would exceed **${Ht} bytes**, do not
826
829
  stuff everything into \`propose\`. Use drafts (expire after **30 minutes**,
827
- max **${j}** decoded bytes):
830
+ max **${H}** decoded bytes):
828
831
 
829
832
  ### Recommended chunk size
830
833
 
@@ -869,7 +872,7 @@ Or a full \`replace_flow\` document. JSON-mode drafts **may add nodes**, so
869
872
  \`draft/new\` does not require an existing \`node\`/\`field\`. Use JSON mode for
870
873
  structural changes; use field-mode only when you need to replace one whole
871
874
  string field that is too large for a single propose URL.
872
- `}a(uu,"docsDrafts");function pu(){return`## Last resort: content larger than chunked drafts
875
+ `}a(Bp,"docsDrafts");function qp(){return`## Last resort: content larger than chunked drafts
873
876
 
874
877
  If the field is still too large for chunked drafts (decoded size over the
875
878
  proposal cap), use this fallback only after drafts fail:
@@ -883,9 +886,9 @@ proposal cap), use this fallback only after drafts fail:
883
886
  **Trade-off:** pasted content is **not** reviewed through the proposal diff.
884
887
  Prefer \`replace_text\` or chunked drafts whenever they fit so the human can
885
888
  still see a readable change on \`/mcp\`.
886
- `}a(pu,"docsLastResortPaste");function fu(){return`## Size cap and REST/MCP nodes payload shapes
889
+ `}a(qp,"docsLastResortPaste");function zp(){return`## Size cap and REST/MCP nodes payload shapes
887
890
 
888
- The proposal JSON cap is **${j} bytes**
891
+ The proposal JSON cap is **${H} bytes**
889
892
  (\`MAX_PROPOSAL_JSON_BYTES\` / ~1.5MB) on **every** channel: GET propose/drafts,
890
893
  REST \`POST /proposals\`, and MCP \`submit_proposal\`. Oversized bodies return
891
894
  \`413 payload_too_large\` (never truncated).
@@ -929,7 +932,7 @@ Example (canonical):
929
932
  **Alternative (also accepted):** top-level \`nodes\` array as an alias for
930
933
  \`payload.nodes\` (same op shapes). Full-tab replace remains supported via
931
934
  \`payload.flow\` (from \`GET ./flows/<id>\` / REST \`GET /flows/{id}\`).
932
- `}a(fu,"docsRestNodesPayload");function mu(){return`## Known issues
935
+ `}a(zp,"docsRestNodesPayload");function Wp(){return`## Known issues
933
936
 
934
937
  - **Rate limits are per token (and per IP).** Each access-link token has its own
935
938
  token bucket. Several tokens can each use the full per-token limit at the same
@@ -937,20 +940,20 @@ Example (canonical):
937
940
  - **\`RateLimit-*\` headers describe the per-token bucket** (default
938
941
  \`RateLimit-Policy: 300;w=60\`) on token requests; requests without a token
939
942
  report the per-IP bucket (\`600;w=60\`). Both are enforced.
940
- `}a(mu,"docsKnownIssues");function hu(){return`## Error codes (JSON unless docs)
943
+ `}a(Wp,"docsKnownIssues");function Gp(){return`## Error codes (JSON unless docs)
941
944
 
942
- ${fi()}
945
+ ${ia()}
943
946
 
944
947
  All JSON responses use \`Cache-Control: no-store\`. Never put secrets in query
945
948
  strings you will paste publicly beyond this temporary link.
946
- `}a(hu,"docsErrorsAndAlso");function Si(e){let{link:t,openapiPath:n,restApiBase:r,mcpRpcPath:o,publicOrigin:s}=e,i=Zt(t.expiresAt),d=t.label?` (${Zt(t.label)})`:"",l=Zt(n),c=Zt(r),u=Zt(o);return[au(d,i),iu(c,l,u,s?Zt(`${s}${o}`):xi),du(),lu(),cu(),fu(),uu(),pu(),mu(),hu()].join(`
947
- `)}a(Si,"renderGetLinkDocs");var Oi=require("crypto");G();K();Fe();Xe();Ae();K();function wr(e){return{error:e instanceof Error&&e.message.trim()?e.message:"Internal server error",code:"internal_error"}}a(wr,"internalErrorBody");function gu(e,t){e.headersSent||e.status(500).json(wr(t))}a(gu,"sendInternalError");function $(e,t){if(e.headersSent)return;let n=O(t);if(n){e.status(n.statusCode).json(n.body);return}gu(e,t)}a($,"sendCaughtRouteError");function R(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}$(n,o)})}}a(R,"asyncRoute");var vr=require("crypto");G();ie();var D=class extends Error{static{a(this,"DraftValidationError")}statusCode=400;code;constructor(t,n="draft_invalid"){super(t),this.name="DraftValidationError",this.code=n}},ht=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"}},gt=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}},yu=/^[A-Za-z0-9_-]+={0,2}$/;function Ti(e){if(!yu.test(e)||e.replace(/=+$/,"").length===0)throw new D("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 D("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data")}if(t.byteLength===0&&e.length>0)throw new D("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data");return t}a(Ti,"assertValidBase64UrlChunk");var br=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,vr.randomBytes)(12).toString("hex")),this.maxBytes=t.maxBytes??j,this.ttlMs=t.ttlMs??Ss}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 gt(t,o.proposalId);let s=this.drafts.get(t);if(s?.linkId!==n)throw new ht(t);if(s.expiresAtMs<=r)throw new ht(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 D("seq must be a non-negative integer","invalid_seq");if(typeof o!="string")throw new D("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 D(`Chunk seq ${r} already stored with different data`,"chunk_conflict")}else{let u=Ti(o),p=s.totalBytes+u.byteLength;if(p>this.maxBytes)throw new D(`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 D("Draft has no chunks","draft_empty");let i=[...o.chunks.keys()].sort((m,h)=>m-h).at(-1);if(i===void 0)throw new D("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 D(`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 D(`Missing chunk seqs: ${m}`,"missing_chunks");l.push(h)}let c=l.map(m=>Ti(m)),u=Buffer.concat(c);if(u.byteLength>this.maxBytes)throw new D(`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 D("invalid_sha256: expected 64 hex chars","invalid_sha256");if((0,vr.createHash)("sha256").update(u).digest("hex")!==p)throw new D("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()}};pr();it();ie();var Ze=ln(require("express-rate-limit"),1);var jo=6e4,Pi=600,Ri=300;function Do(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?Math.max(1,Math.floor(e)):t}a(Do,"positiveInt");function He(e){if(!e)return{windowMs:jo,maxPerIp:Pi,maxPerToken:Ri};let t=e;return{windowMs:Do(t.windowMs??t.rateLimitWindowMs,jo),maxPerIp:Do(t.maxPerIp??t.rateLimitMaxPerIp,Pi),maxPerToken:Do(t.maxPerToken??t.rateLimitMaxPerToken,Ri)}}a(He,"resolveRateLimitConfig");function wu(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(wu,"retryAfterSeconds");function _i(e,t,n=jo){let r=wu(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(_i,"sendRateLimited");function bu(e){return(t,n,r,o)=>{_i(t,n,e)}}a(bu,"rateLimitedHandler");function kr(e){return`ip:${(0,Ze.ipKeyGenerator)(e.ip??"unknown")}`}a(kr,"clientIpKey");function Ar(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(Ar,"accessTokenRateLimitKey");function Li(e,t,n,r){let o={windowMs:t,max:e,standardHeaders:!0,legacyHeaders:!1,keyGenerator:n,handler:bu(t)};return r&&(o.skip=r),(0,Ze.default)(o)}a(Li,"createLimiter");function Ho(e){let t=He(e);return Li(t.maxPerIp,t.windowMs,kr)}a(Ho,"createAiAccessRateLimiter");function vu(e){let t=He(e);return Li(t.maxPerToken,t.windowMs,n=>Ar(n)??kr(n),n=>Ar(n)==null)}a(vu,"createAiAccessTokenRateLimiter");function Ci(e){let t=He(e),n=Ho(t),r=vu(t);return{ip:n,token:r,stack:[n,r],config:t}}a(Ci,"createAiAccessDualRateLimiters");function Ei(e){let t=He(e()).windowMs,n=He(e()).maxPerIp,r=He(e()).maxPerToken,o=new Ze.MemoryStore,s=new Ze.MemoryStore,i=a(()=>{let h=He(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=((x,S)=>(x.toLowerCase()==="ratelimit-policy"&&typeof S=="string"&&(S=S.replace(/;w=\d+/,`;w=${Math.ceil(t/1e3)}`)),g(x,S)))},"useLiveWindowPolicy"),c=a((h,g)=>(0,Ze.default)({store:g,windowMs:t,limit:a(()=>{let x=He(e());return h?x.maxPerToken:x.maxPerIp},"limit"),standardHeaders:!0,legacyHeaders:!1,keyGenerator:h?x=>Ar(x)??kr(x):kr,skip:a(x=>h&&Ar(x)==null,"skip"),handler:a((x,S,E)=>{_i(x,S,He(e()).windowMs)},"handler")}),"make"),u=c(!1,o),p=c(!0,s);return{ip:a((h,g,x)=>(i(),l(g),u(h,g,x)),"ip"),token:a((h,g,x)=>(i(),l(g),p(h,g,x)),"token")}}a(Ei,"createLiveAiAccessDualRateLimiters");ye();function F(e){e.setHeader("Cache-Control","no-store"),e.setHeader("Pragma","no-cache")}a(F,"setNoStore");function Uo(e){let t=e.originalUrl||e.url||"",n=so(t);n!==t&&(Object.defineProperty(e,"originalUrl",{value:n,writable:!0,configurable:!0}),typeof e.url=="string"&&Object.defineProperty(e,"url",{value:so(e.url),writable:!0,configurable:!0}))}a(Uo,"redactTokenFromRequestUrl");function Ii(e,t){return(n,r,o)=>{F(r);let s=n.originalUrl||n.url||"";if(n.getLinkUrlBytes=Buffer.byteLength(s,"utf8"),Uo(n),!ge(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",Y),r.status(401).json({error:le,code:Le,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"){wn(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",Y),r.status(401).json({error:st,code:yn,hint:c});return}if(l.link.kind!=="get_link"){bn(r,"get_link",l.link.kind);return}n.accessLink=l.link,n.getLinkToken=d,o()}}a(Ii,"requireGetLinkToken");function en(e,t){let n=e.getLinkUrlBytes;return(typeof n=="number"?n:Buffer.byteLength(e.originalUrl||e.url||"","utf8"))>It?(F(t),t.status(414).json({error:`Request URL exceeds ${It} 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:It}),!0):!1}a(en,"rejectOversizedGetUrl");function C(e,t,n,r,o,s){F(e),e.status(t).json({error:n,code:r,...o?{hint:o}:{},...s})}a(C,"jsonError");function Ni(){return(e,t)=>{Uo(e),F(t),t.status(404).json({error:"Not found",code:"not_found"})}}a(Ni,"getLinkNotFoundHandler");var ku="link_create_not_supported";function Fi(){return(e,t)=>{Uo(e),F(t),t.status(403).json({error:"GET access links cannot create tokens",code:ku,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(Fi,"getLinkMintNotSupportedHandler");function _(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(_,"queryString");function Se(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(Se,"draftErrorHint");async function Au(e,t,n){let r;try{r=await e.getFlow(t)}catch(s){let i=O(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(Au,"validateFieldDraftTargets");function xu(e,t,n,r){if(r)try{let o=Pn(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=Se("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(xu,"planDraftMode");async function Su(e,t,n,r){try{let o=await Au(t,n,r);if(o){let s=o.code==="node_not_found"||o.code==="field_not_found"?404:400;return C(e,s,o.message,o.code,Se(o.code)),!0}}catch(o){let s=O(o);return s?(C(e,s.statusCode,s.body.error,s.body.code,Se(s.body.code)),!0):($(e,o),!0)}return!1}a(Su,"rejectInvalidFieldTargets");function $i(e,t){return async(n,r)=>{if(en(n,r))return;F(r);let o=_(n.query,"flow"),s=_(n.query,"rev"),i=_(n.query,"node"),d=_(n.query,"field"),l=_(n.query,"summary"),c=_(n.query,"title")??l,u=_(n.query,"mode"),p=_(n.query,"targets");if(!o||!s){C(r,400,"flow and rev query parameters are required","missing_draft_params",Se("missing_draft_params"));return}if(!n.accessLink){C(r,401,"Access link required","access_link_missing");return}try{let h=await t.getFlow(o);if(h.rev!==s){r.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:s,currentRev:h.rev,hint:Se("version_mismatch")});return}}catch(h){let g=O(h);if(g){C(r,g.statusCode,g.body.error,g.body.code,g.body.code==="flow_not_found"?se("flow_not_found","get")??"Use an id from GET ./flows":Se(g.body.code));return}$(r,h);return}let f=xu(i,d,u,p);if(!f.ok){C(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 Su(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:j,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($i,"getLinkDraftNewHandler");function Mi(e){return(t,n)=>{if(en(t,n))return;F(n);let r=t.params.draftId,o=_(t.query,"seq"),s=_(t.query,"data");if(typeof r!="string"||!r){C(n,400,"Missing draft id","missing_draft_id");return}if(!t.accessLink){C(n,401,"Access link required","access_link_missing");return}if(o===void 0||s===void 0){C(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 ht||d instanceof gt||d instanceof D){C(n,d.statusCode,d.message,d.code,Se(d.code),d instanceof gt?{proposalId:d.proposalId}:void 0);return}$(n,d);return}}}a(Mi,"getLinkDraftAppendHandler");function Tu(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 D("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 D("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 D(`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 D("Invalid multi-target draft JSON","invalid_draft_body")}if(e.nodeId&&e.field)return[{nodeId:e.nodeId,set:{[e.field]:t}}];throw new D("Field draft missing node/field","invalid_draft")}a(Tu,"editsFromFieldDraft");function Di(e,t,n,r,o,s){return async(i,d)=>{if(en(i,d))return;F(d);let l=i.params.draftId,c=_(i.query,"sha256"),u=_(i.query,"title");if(typeof l!="string"||!l){C(d,400,"Missing draft id","missing_draft_id");return}if(!i.accessLink){C(d,401,"Access link required","access_link_missing");return}if(!c){C(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;if(p.mode==="field"){let A=Tu(p,f),v=await Tn(r,p.flowId,A);if(v.expectedRev!==p.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:p.expectedRev,currentRev:v.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=v.flow,g=v.expectedRev}else{let A=Rn(JSON.parse(f),p.flowId);if(m=A.flowId,A.kind==="replace_flow")h={...A.flow,id:A.flow.id||m,type:A.flow.type??"tab",nodes:A.flow.nodes??[]};else{let v=await pt(r,m,A.ops,{expectedRev:p.expectedRev,...s?{nodeTypes:s}:{}});if(v.expectedRev!==p.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:p.expectedRev,currentRev:v.expectedRev,draftDeleted:!1,hint:"Draft was kept. Re-read the flow and start a new draft with the current rev."});return}h=v.flow,g=v.expectedRev}}let x=u??p.title??p.summary,S=q(i,o),E=re(i.query),I=await Ye(t,n,i.accessLink,{type:"replace_flow",expectedRev:g,payload:{flowId:m,flow:h},...x?{title:x}:{}},r,S,s);e.markSubmitted(l,i.accessLink.id,I.id);let N={...ce(I,{...S,include:E}),mine:!0},ot=V(t,I.expectedRev,{flowId:I.payload.flowId}),me=Ie(t,t.get(I.id),i.accessLink.id);d.status(201).json({ok:!0,proposal:N,sha256:(0,Oi.createHash)("sha256").update(Buffer.from(f,"utf8")).digest("hex"),...Ne(me),...Q(ot),hint:`Waiting for human approval. Tell the user about "${N.displayName}" (${N.reviewUrl}). Poll GET \u2026/proposals/<id> for status.`})}catch(p){if(p instanceof gt){C(d,p.statusCode,p.message,p.code,Se(p.code),{proposalId:p.proposalId});return}if(p instanceof ht||p instanceof D){C(d,p.statusCode,p.message,p.code,Se(p.code));return}if(p instanceof SyntaxError){C(d,400,"Assembled draft is not valid JSON","invalid_draft_body");return}let f=O(p);if(f){d.status(f.statusCode).json({...f.body,...Se(f.body.code)?{hint:Se(f.body.code)}:{}});return}$(d,p);return}}}a(Di,"getLinkDraftSubmitHandler");K();Fe();gr();Xe();Ae();pr();var Pu={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 Bo(e,t){return se(e,"get",{cause:t})??Pu[e]??Cn(e)}a(Bo,"proposeErrorHint");function Ru(e,t,n){e.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:t,currentRev:n,hint:Bo("version_mismatch")})}a(Ru,"sendVersionMismatch");async function _u(e,t,n,r,o){let s=Rn(Pn(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 pt(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(_u,"buildFromPatch");async function Lu(e,t,n,r,o){let s=await Tn(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(Lu,"buildFromSetProps");async function Cu(e,t,n,r,o,s,i,d){let l=await pt(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(Cu,"buildFromReplaceText");function Eu(e){let t=Object.prototype.hasOwnProperty.call(e.query,"replace");return{flowId:_(e.query,"flow"),rev:_(e.query,"rev"),nodeId:_(e.query,"node"),patchEncoded:_(e.query,"patch"),setProps:_o(e.query),findText:_(e.query,"find"),replaceText:t?_(e.query,"replace")??"":void 0,fieldName:_(e.query,"field"),occurrenceRaw:_(e.query,"occurrence")}}a(Eu,"readProposeQuery");function Te(e,t,n){C(e,400,n,t,Bo(t))}a(Te,"jsonProposeError");function Iu(e,t){if(t===void 0||t==="")return{ok:!0};let n=Number(t);return Number.isFinite(n)?{ok:!0,occurrence:Math.trunc(n)}:(Te(e,"invalid_occurrence","occurrence must be a positive integer"),{ok:!1})}a(Iu,"parseOccurrenceQuery");async function Nu(e,t,n,r,o){if(n.patchEncoded)return _u(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 Te(e,"conflicting_edits","Cannot combine replace_text (find/replace/field) with set.<prop> in one request"),null;if(!n.flowId)return Te(e,"missing_flow_id","flow query parameter is required"),null;if(!n.nodeId)return Te(e,"missing_node_id","node query parameter is required"),null;if(!n.fieldName)return Te(e,"missing_field","field query parameter is required for replace_text"),null;if(n.findText===void 0||n.findText.length===0)return Te(e,"missing_find","find query parameter must be a non-empty string"),null;if(n.replaceText===void 0)return Te(e,"invalid_replace","replace query parameter is required (may be empty)"),null;let i=Iu(e,n.occurrenceRaw);return i.ok?Cu(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?(Te(e,"missing_edits","At least one set.<prop>=value is required"),null):Lu(t,n.flowId,n.nodeId,n.setProps,r):(Te(e,"missing_node_id","node query parameter is required (or pass patch=)"),null):(Te(e,"missing_flow_id","flow query parameter is required"),null)}a(Nu,"buildProposeFromQuery");function ji(e,t,n,r,o){return async(s,i)=>{if(en(s,i))return;F(i);let d=Eu(s),l=_(s.query,"summary"),c=_(s.query,"title")??l;if(!d.rev){Te(i,"missing_expected_rev","rev query parameter is required");return}if(!s.accessLink){C(i,401,"Access link required","access_link_missing");return}try{let u=await Nu(i,n,d,d.rev,o);if(!u)return;if(!u.ok){Ru(i,u.expectedRev,u.currentRev);return}let p=q(s,r),f=re(s.query),m=await Ye(e,t,s.accessLink,{type:"replace_flow",expectedRev:u.expectedRev,payload:{flowId:u.builtFlowId,flow:u.builtFlow},...c?{title:c}:{}},n,p,o),h={...ce(m,{...p,include:f}),mine:!0},g=V(e,m.expectedRev,{flowId:m.payload.flowId}),x=Ie(e,e.get(m.id),s.accessLink.id),S=m.warnings;i.status(201).json({ok:!0,proposal:h,...l?{summary:l}:{},...Ne(x),...Q(g),...S&&S.length>0?{warnings:S}:{},hint:`Waiting for human approval. Tell the user about "${h.displayName}" (${h.reviewUrl}). Poll GET \u2026/proposals/<id> for status.${S&&S.length>0?` Soft warnings (${S.length}) are non-blocking \u2014 configure later if needed.`:""}`})}catch(u){let p=O(u,"get");if(p){let f=Bo(p.body.code,p.body.cause);i.status(p.statusCode).json({...p.body,...f?{hint:f}:{}});return}$(i,u)}}}a(ji,"getLinkProposeHandler");K();Fe();K();var Fu=50,Hi=40,Ou=20;function $u(e){return Buffer.byteLength(JSON.stringify(e),"utf8")}a($u,"nodeBytes");function Mu(e){let t={id:e.id,type:e.type,bytes:$u(e)};return typeof e.name=="string"&&(t.name=e.name),"wires"in e&&(t.wires=e.wires),t}a(Mu,"summarizeNode");async function xr(e,t){let{rev:n,flow:r}=await xe(e,t),o=r,s=Array.isArray(o.nodes)?o.nodes.map(Mu):[],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(xr,"getFlowSummary");function Ui(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(Ui,"collectTabs");async function Bi(e,t,n){if(!t)throw new y("Missing node id","missing_node_id");if(n){let{rev:d,flow:l}=await xe(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 y("Node was not found","node_not_found",{statusCode:404});return{rev:d,flowId:n,node:f}}let{rev:r,flows:o}=await pe(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 Ui(o)){let l=d.id;if(l)try{let u=(await xe(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 y("Node was not found","node_not_found",{statusCode:404});return{rev:r,flowId:i,node:s}}a(Bi,"findNodeInFlows");async function Sr(e,t,n){let r=await Bi(e,t,n);return{rev:r.rev,flowId:r.flowId,node:r.node}}a(Sr,"getNodeById");function Du(e){if(e===void 0||e==="")return;let t=/^(\d+)(?:-(\d+))?$/.exec(e.trim());if(!t)throw new y("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 y("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");if(r<n)throw new y("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");return{start:n,end:r}}a(Du,"parseLineRange");async function Tr(e,t,n,r={}){if(!n)throw new y("Missing field name","missing_field");let o=await Bi(e,t,r.flowId);if(!(n in o.node))throw new y("Field was not found on the node","field_not_found");let s=o.node[n];if(typeof s!="string")throw new y("Field is not a string","field_not_string");let i=s.split(`
948
- `),d=i.length,l=Buffer.byteLength(s,"utf8"),c=Du(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 y(`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(`
949
- `);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(Tr,"getNodeField");function ju(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(ju,"stringFields");function Hu(e,t,n){let r=Math.max(0,t-Hi),o=Math.min(e.length,t+n+Hi),s=e.slice(r,o).replace(/\s+/g," ");return r>0&&(s=`\u2026${s}`),o<e.length&&(s=`${s}\u2026`),s}a(Hu,"snippetAround");function Uu(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(Uu,"lineNumberAtIndex");function qo(e,t,n,r,o){for(let{field:s,value:i}of ju(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(`
949
+ `}a(Gp,"docsErrorsAndAlso");function ya(e){let{link:t,openapiPath:n,restApiBase:r,mcpRpcPath:o,publicOrigin:s}=e,i=dn(t.expiresAt),d=t.label?` (${dn(t.label)})`:"",l=dn(n),c=dn(r),u=dn(o);return[Dp(d,i),Mp(c,l,u,s?dn(`${s}${o}`):ga),jp(),Hp(),Up(),zp(),Bp(),qp(),Wp(),Gp()].join(`
950
+ `)}a(ya,"renderGetLinkDocs");var Pa=require("crypto");G();J();De();tt();Re();J();function Ur(e){return{error:e instanceof Error&&e.message.trim()?e.message:"Internal server error",code:"internal_error"}}a(Ur,"internalErrorBody");function Kp(e,t){e.headersSent||e.status(500).json(Ur(t))}a(Kp,"sendInternalError");function D(e,t){if(e.headersSent)return;let n=M(t);if(n){e.status(n.statusCode).json(n.body);return}Kp(e,t)}a(D,"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}D(n,o)})}}a(_,"asyncRoute");var qr=require("crypto");G();ce();var j=class extends Error{static{a(this,"DraftValidationError")}statusCode=400;code;constructor(t,n="draft_invalid"){super(t),this.name="DraftValidationError",this.code=n}},St=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"}},xt=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}},Jp=/^[A-Za-z0-9_-]+={0,2}$/;function wa(e){if(!Jp.test(e)||e.replace(/=+$/,"").length===0)throw new j("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 j("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data")}if(t.byteLength===0&&e.length>0)throw new j("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data");return t}a(wa,"assertValidBase64UrlChunk");var Br=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,qr.randomBytes)(12).toString("hex")),this.maxBytes=t.maxBytes??H,this.ttlMs=t.ttlMs??Zs}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 xt(t,o.proposalId);let s=this.drafts.get(t);if(s?.linkId!==n)throw new St(t);if(s.expiresAtMs<=r)throw new St(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 j("seq must be a non-negative integer","invalid_seq");if(typeof o!="string")throw new j("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 j(`Chunk seq ${r} already stored with different data`,"chunk_conflict")}else{let u=wa(o),p=s.totalBytes+u.byteLength;if(p>this.maxBytes)throw new j(`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 j("Draft has no chunks","draft_empty");let i=[...o.chunks.keys()].sort((m,h)=>m-h).at(-1);if(i===void 0)throw new j("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 j(`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 j(`Missing chunk seqs: ${m}`,"missing_chunks");l.push(h)}let c=l.map(m=>wa(m)),u=Buffer.concat(c);if(u.byteLength>this.maxBytes)throw new j(`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 j("invalid_sha256: expected 64 hex chars","invalid_sha256");if((0,qr.createHash)("sha256").update(u).digest("hex")!==p)throw new j("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()}};Or();pt();ce();var st=yn(require("express-rate-limit"),1);var us=6e4,ba=600,va=300;function cs(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?Math.max(1,Math.floor(e)):t}a(cs,"positiveInt");function Ge(e){if(!e)return{windowMs:us,maxPerIp:ba,maxPerToken:va};let t=e;return{windowMs:cs(t.windowMs??t.rateLimitWindowMs,us),maxPerIp:cs(t.maxPerIp??t.rateLimitMaxPerIp,ba),maxPerToken:cs(t.maxPerToken??t.rateLimitMaxPerToken,va)}}a(Ge,"resolveRateLimitConfig");function Vp(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(Vp,"retryAfterSeconds");function ka(e,t,n=us){let r=Vp(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(ka,"sendRateLimited");function Yp(e){return(t,n,r,o)=>{ka(t,n,e)}}a(Yp,"rateLimitedHandler");function zr(e){return`ip:${(0,st.ipKeyGenerator)(e.ip??"unknown")}`}a(zr,"clientIpKey");function Wr(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(Wr,"accessTokenRateLimitKey");function Aa(e,t,n,r){let o={windowMs:t,max:e,standardHeaders:!0,legacyHeaders:!1,keyGenerator:n,handler:Yp(t)};return r&&(o.skip=r),(0,st.default)(o)}a(Aa,"createLimiter");function ps(e){let t=Ge(e);return Aa(t.maxPerIp,t.windowMs,zr)}a(ps,"createAiAccessRateLimiter");function Xp(e){let t=Ge(e);return Aa(t.maxPerToken,t.windowMs,n=>Wr(n)??zr(n),n=>Wr(n)==null)}a(Xp,"createAiAccessTokenRateLimiter");function Sa(e){let t=Ge(e),n=ps(t),r=Xp(t);return{ip:n,token:r,stack:[n,r],config:t}}a(Sa,"createAiAccessDualRateLimiters");function xa(e){let t=Ge(e()).windowMs,n=Ge(e()).maxPerIp,r=Ge(e()).maxPerToken,o=new st.MemoryStore,s=new st.MemoryStore,i=a(()=>{let h=Ge(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=((k,T)=>(k.toLowerCase()==="ratelimit-policy"&&typeof T=="string"&&(T=T.replace(/;w=\d+/,`;w=${Math.ceil(t/1e3)}`)),g(k,T)))},"useLiveWindowPolicy"),c=a((h,g)=>(0,st.default)({store:g,windowMs:t,limit:a(()=>{let k=Ge(e());return h?k.maxPerToken:k.maxPerIp},"limit"),standardHeaders:!0,legacyHeaders:!1,keyGenerator:h?k=>Wr(k)??zr(k):zr,skip:a(k=>h&&Wr(k)==null,"skip"),handler:a((k,T,C)=>{ka(k,T,Ge(e()).windowMs)},"handler")}),"make"),u=c(!1,o),p=c(!0,s);return{ip:a((h,g,k)=>(i(),l(g),u(h,g,k)),"ip"),token:a((h,g,k)=>(i(),l(g),p(h,g,k)),"token")}}a(xa,"createLiveAiAccessDualRateLimiters");ue();function O(e){e.setHeader("Cache-Control","no-store"),e.setHeader("Pragma","no-cache")}a(O,"setNoStore");function fs(e){let t=e.originalUrl||e.url||"",n=Po(t);n!==t&&(Object.defineProperty(e,"originalUrl",{value:n,writable:!0,configurable:!0}),typeof e.url=="string"&&Object.defineProperty(e,"url",{value:Po(e.url),writable:!0,configurable:!0}))}a(fs,"redactTokenFromRequestUrl");function Ta(e,t){return(n,r,o)=>{O(r);let s=n.originalUrl||n.url||"";if(n.getLinkUrlBytes=Buffer.byteLength(s,"utf8"),fs(n),!ke(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",X),r.status(401).json({error:me,code:Ie,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"){En(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",X),r.status(401).json({error:ut,code:Pn,hint:c});return}if(l.link.kind!=="get_link"){Cn(r,"get_link",l.link.kind);return}n.accessLink=l.link,n.getLinkToken=d,o()}}a(Ta,"requireGetLinkToken");function ln(e,t){let n=e.getLinkUrlBytes;return(typeof n=="number"?n:Buffer.byteLength(e.originalUrl||e.url||"","utf8"))>Ht?(O(t),t.status(414).json({error:`Request URL exceeds ${Ht} 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:Ht}),!0):!1}a(ln,"rejectOversizedGetUrl");function I(e,t,n,r,o,s){O(e),e.status(t).json({error:n,code:r,...o?{hint:o}:{},...s})}a(I,"jsonError");function Ra(){return(e,t)=>{fs(e),O(t),t.status(404).json({error:"Not found",code:"not_found"})}}a(Ra,"getLinkNotFoundHandler");var Zp="link_create_not_supported";function _a(){return(e,t)=>{fs(e),O(t),t.status(403).json({error:"GET access links cannot create tokens",code:Zp,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(_a,"getLinkMintNotSupportedHandler");function P(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(P,"queryString");function Pe(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(Pe,"draftErrorHint");async function Qp(e,t,n){let r;try{r=await e.getFlow(t)}catch(s){let i=M(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(Qp,"validateFieldDraftTargets");function ef(e,t,n,r){if(r)try{let o=Hn(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=Pe("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(ef,"planDraftMode");async function tf(e,t,n,r){try{let o=await Qp(t,n,r);if(o){let s=o.code==="node_not_found"||o.code==="field_not_found"?404:400;return I(e,s,o.message,o.code,Pe(o.code)),!0}}catch(o){let s=M(o);return s?(I(e,s.statusCode,s.body.error,s.body.code,Pe(s.body.code)),!0):(D(e,o),!0)}return!1}a(tf,"rejectInvalidFieldTargets");function Ea(e,t){return async(n,r)=>{if(ln(n,r))return;O(r);let o=P(n.query,"flow"),s=P(n.query,"rev"),i=P(n.query,"node"),d=P(n.query,"field"),l=P(n.query,"summary"),c=P(n.query,"title")??l,u=P(n.query,"mode"),p=P(n.query,"targets");if(!o||!s){I(r,400,"flow and rev query parameters are required","missing_draft_params",Pe("missing_draft_params"));return}if(!n.accessLink){I(r,401,"Access link required","access_link_missing");return}try{let h=await t.getFlow(o);if(h.rev!==s){r.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:s,currentRev:h.rev,hint:Pe("version_mismatch")});return}}catch(h){let g=M(h);if(g){I(r,g.statusCode,g.body.error,g.body.code,g.body.code==="flow_not_found"?de("flow_not_found","get")??"Use an id from GET ./flows":Pe(g.body.code));return}D(r,h);return}let f=ef(i,d,u,p);if(!f.ok){I(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 tf(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:H,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(Ea,"getLinkDraftNewHandler");function Ca(e){return(t,n)=>{if(ln(t,n))return;O(n);let r=t.params.draftId,o=P(t.query,"seq"),s=P(t.query,"data");if(typeof r!="string"||!r){I(n,400,"Missing draft id","missing_draft_id");return}if(!t.accessLink){I(n,401,"Access link required","access_link_missing");return}if(o===void 0||s===void 0){I(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 St||d instanceof xt||d instanceof j){I(n,d.statusCode,d.message,d.code,Pe(d.code),d instanceof xt?{proposalId:d.proposalId}:void 0);return}D(n,d);return}}}a(Ca,"getLinkDraftAppendHandler");function nf(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 j("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 j("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 j(`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 j("Invalid multi-target draft JSON","invalid_draft_body")}if(e.nodeId&&e.field)return[{nodeId:e.nodeId,set:{[e.field]:t}}];throw new j("Field draft missing node/field","invalid_draft")}a(nf,"editsFromFieldDraft");function La(e,t,n,r,o,s){return async(i,d)=>{if(ln(i,d))return;O(d);let l=i.params.draftId,c=P(i.query,"sha256"),u=P(i.query,"title");if(typeof l!="string"||!l){I(d,400,"Missing draft id","missing_draft_id");return}if(!i.accessLink){I(d,401,"Access link required","access_link_missing");return}if(!c){I(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;if(p.mode==="field"){let S=nf(p,f),b=await jn(r,p.flowId,S);if(b.expectedRev!==p.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:p.expectedRev,currentRev:b.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=b.flow,g=b.expectedRev}else{let S=Un(JSON.parse(f),p.flowId);if(m=S.flowId,S.kind==="replace_flow")h={...S.flow,id:S.flow.id||m,type:S.flow.type??"tab",nodes:S.flow.nodes??[]};else{let b=await vt(r,m,S.ops,{expectedRev:p.expectedRev,...s?{nodeTypes:s}:{}});if(b.expectedRev!==p.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:p.expectedRev,currentRev:b.expectedRev,draftDeleted:!1,hint:"Draft was kept. Re-read the flow and start a new draft with the current rev."});return}h=b.flow,g=b.expectedRev}}let k=u??p.title??p.summary,T=q(i,o),C=ie(i.query),L=await rt(t,n,i.accessLink,{type:"replace_flow",expectedRev:g,payload:{flowId:m,flow:h},...k?{title:k}:{}},r,T,s);e.markSubmitted(l,i.accessLink.id,L.id);let N={...he(L,{...T,include:C}),mine:!0},Ue=V(t,L.expectedRev,{flowId:L.payload.flowId}),Ce=Oe(t,t.get(L.id),i.accessLink.id);d.status(201).json({ok:!0,proposal:N,sha256:(0,Pa.createHash)("sha256").update(Buffer.from(f,"utf8")).digest("hex"),...$e(Ce),...Z(Ue),hint:`Waiting for human approval. Tell the user about "${N.displayName}" (${N.reviewUrl}). Poll GET \u2026/proposals/<id> for status.`})}catch(p){if(p instanceof xt){I(d,p.statusCode,p.message,p.code,Pe(p.code),{proposalId:p.proposalId});return}if(p instanceof St||p instanceof j){I(d,p.statusCode,p.message,p.code,Pe(p.code));return}if(p instanceof SyntaxError){I(d,400,"Assembled draft is not valid JSON","invalid_draft_body");return}let f=M(p);if(f){d.status(f.statusCode).json({...f.body,...Pe(f.body.code)?{hint:Pe(f.body.code)}:{}});return}D(d,p);return}}}a(La,"getLinkDraftSubmitHandler");J();De();jr();tt();Re();Or();var rf={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 ms(e,t){return de(e,"get",{cause:t})??rf[e]??zn(e)}a(ms,"proposeErrorHint");function of(e,t,n){e.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:t,currentRev:n,hint:ms("version_mismatch")})}a(of,"sendVersionMismatch");async function sf(e,t,n,r,o){let s=Un(Hn(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 vt(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(sf,"buildFromPatch");async function af(e,t,n,r,o){let s=await jn(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(af,"buildFromSetProps");async function df(e,t,n,r,o,s,i,d){let l=await vt(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(df,"buildFromReplaceText");function lf(e){let t=Object.prototype.hasOwnProperty.call(e.query,"replace");return{flowId:P(e.query,"flow"),rev:P(e.query,"rev"),nodeId:P(e.query,"node"),patchEncoded:P(e.query,"patch"),setProps:es(e.query),findText:P(e.query,"find"),replaceText:t?P(e.query,"replace")??"":void 0,fieldName:P(e.query,"field"),occurrenceRaw:P(e.query,"occurrence")}}a(lf,"readProposeQuery");function Ee(e,t,n){I(e,400,n,t,ms(t))}a(Ee,"jsonProposeError");function cf(e,t){if(t===void 0||t==="")return{ok:!0};let n=Number(t);return Number.isFinite(n)?{ok:!0,occurrence:Math.trunc(n)}:(Ee(e,"invalid_occurrence","occurrence must be a positive integer"),{ok:!1})}a(cf,"parseOccurrenceQuery");async function uf(e,t,n,r,o){if(n.patchEncoded)return sf(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 Ee(e,"conflicting_edits","Cannot combine replace_text (find/replace/field) with set.<prop> in one request"),null;if(!n.flowId)return Ee(e,"missing_flow_id","flow query parameter is required"),null;if(!n.nodeId)return Ee(e,"missing_node_id","node query parameter is required"),null;if(!n.fieldName)return Ee(e,"missing_field","field query parameter is required for replace_text"),null;if(n.findText===void 0||n.findText.length===0)return Ee(e,"missing_find","find query parameter must be a non-empty string"),null;if(n.replaceText===void 0)return Ee(e,"invalid_replace","replace query parameter is required (may be empty)"),null;let i=cf(e,n.occurrenceRaw);return i.ok?df(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?(Ee(e,"missing_edits","At least one set.<prop>=value is required"),null):af(t,n.flowId,n.nodeId,n.setProps,r):(Ee(e,"missing_node_id","node query parameter is required (or pass patch=)"),null):(Ee(e,"missing_flow_id","flow query parameter is required"),null)}a(uf,"buildProposeFromQuery");function Ia(e,t,n,r,o){return async(s,i)=>{if(ln(s,i))return;O(i);let d=lf(s),l=P(s.query,"summary"),c=P(s.query,"title")??l;if(!d.rev){Ee(i,"missing_expected_rev","rev query parameter is required");return}if(!s.accessLink){I(i,401,"Access link required","access_link_missing");return}try{let u=await uf(i,n,d,d.rev,o);if(!u)return;if(!u.ok){of(i,u.expectedRev,u.currentRev);return}let p=q(s,r),f=ie(s.query),m=await rt(e,t,s.accessLink,{type:"replace_flow",expectedRev:u.expectedRev,payload:{flowId:u.builtFlowId,flow:u.builtFlow},...c?{title:c}:{}},n,p,o),h={...he(m,{...p,include:f}),mine:!0},g=V(e,m.expectedRev,{flowId:m.payload.flowId}),k=Oe(e,e.get(m.id),s.accessLink.id),T=m.warnings;i.status(201).json({ok:!0,proposal:h,...l?{summary:l}:{},...$e(k),...Z(g),...T&&T.length>0?{warnings:T}:{},hint:`Waiting for human approval. Tell the user about "${h.displayName}" (${h.reviewUrl}). Poll GET \u2026/proposals/<id> for status.${T&&T.length>0?` Soft warnings (${T.length}) are non-blocking \u2014 configure later if needed.`:""}`})}catch(u){let p=M(u,"get");if(p){let f=ms(p.body.code,p.body.cause);i.status(p.statusCode).json({...p.body,...f?{hint:f}:{}});return}D(i,u)}}}a(Ia,"getLinkProposeHandler");J();De();J();var pf=50,Na=40,ff=20;function mf(e){return Buffer.byteLength(JSON.stringify(e),"utf8")}a(mf,"nodeBytes");function hf(e){let t={id:e.id,type:e.type,bytes:mf(e)};return typeof e.name=="string"&&(t.name=e.name),"wires"in e&&(t.wires=e.wires),t}a(hf,"summarizeNode");async function Gr(e,t){let{rev:n,flow:r}=await _e(e,t),o=r,s=Array.isArray(o.nodes)?o.nodes.map(hf):[],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(Gr,"getFlowSummary");function Fa(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(Fa,"collectTabs");async function Oa(e,t,n){if(!t)throw new y("Missing node id","missing_node_id");if(n){let{rev:d,flow:l}=await _e(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 y("Node was not found","node_not_found",{statusCode:404});return{rev:d,flowId:n,node:f}}let{rev:r,flows:o}=await ye(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 Fa(o)){let l=d.id;if(l)try{let u=(await _e(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 y("Node was not found","node_not_found",{statusCode:404});return{rev:r,flowId:i,node:s}}a(Oa,"findNodeInFlows");async function Kr(e,t,n){let r=await Oa(e,t,n);return{rev:r.rev,flowId:r.flowId,node:r.node}}a(Kr,"getNodeById");function gf(e){if(e===void 0||e==="")return;let t=/^(\d+)(?:-(\d+))?$/.exec(e.trim());if(!t)throw new y("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 y("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");if(r<n)throw new y("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");return{start:n,end:r}}a(gf,"parseLineRange");async function Jr(e,t,n,r={}){if(!n)throw new y("Missing field name","missing_field");let o=await Oa(e,t,r.flowId);if(!(n in o.node))throw new y("Field was not found on the node","field_not_found");let s=o.node[n];if(typeof s!="string")throw new y("Field is not a string","field_not_string");let i=s.split(`
951
+ `),d=i.length,l=Buffer.byteLength(s,"utf8"),c=gf(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 y(`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(`
952
+ `);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(Jr,"getNodeField");function yf(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(yf,"stringFields");function wf(e,t,n){let r=Math.max(0,t-Na),o=Math.min(e.length,t+n+Na),s=e.slice(r,o).replace(/\s+/g," ");return r>0&&(s=`\u2026${s}`),o<e.length&&(s=`${s}\u2026`),s}a(wf,"snippetAround");function bf(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(bf,"lineNumberAtIndex");function hs(e,t,n,r,o){for(let{field:s,value:i}of yf(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(`
950
953
  `)){let f=i.split(`
951
- `);for(let m=0;m<f.length;m+=1){let g=(f[m]??"").indexOf(n);if(g>=0){let x=0;for(let S=0;S<m;S+=1)x+=(f[S]?.length??0)+1;u(m+1,x+g)}}}if(n.includes(`
952
- `)){let f=0;for(;f<=i.length;){let m=i.indexOf(n,f);if(m<0)break;u(Uu(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,Ou),lineCount:p,snippet:Hu(i,Math.max(0,c),n.length)})}return r.length>=o}a(qo,"searchInNode");function qi(e){return[...e.nodes??[],...e.configs??[]]}a(qi,"nodePool");async function Bu(e,t,n,r,o){let s=await xe(e,t);for(let i of qi(s.flow))if(qo(i,t,n,r,o))return{rev:s.rev,truncated:!0};return{rev:s.rev,truncated:!1}}a(Bu,"searchOneFlow");async function qu(e,t,n,r){let o=await pe(e),s=new Set;for(let i of Ui(o.flows))if(i.id)try{let d=await xe(e,i.id);for(let l of qi(d.flow))if(s.add(l.id),qo(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(qo(d,l,t,n,r))return{rev:o.rev,truncated:!0}}return{rev:o.rev,truncated:!1}}a(qu,"searchAllFlows");async function Pr(e,t,n={}){if(typeof t!="string"||!t.trim())throw new y("q query parameter is required","missing_query");let r=typeof n.cap=="number"&&n.cap>0?Math.floor(n.cap):Fu,o=[],s=n.flowId?await Bu(e,n.flowId,t,o,r):await qu(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(Pr,"searchFlows");function Wi(e){if(e===void 0)return!1;let t=e.toLowerCase();return t==="1"||t==="true"||t==="yes"}a(Wi,"isTruthyQueryFlag");function Wu(e,t,n){switch(e){case"node_not_found":case"flow_not_found":case"field_not_found":return n??se(e,t);default:return se(e,t)}}a(Wu,"layeredReadErrorHint");function yt(e){return Oe[e]??"Request failed"}a(yt,"safeErrorText");function fe(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(fe,"sendJson");function Rr(e,t,n,r){let o=O(t,n);if(!o)return!1;let s=o.body.code,i={error:yt(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=Wu(s,n,r);return d&&(i.hint=d),fe(e,o.statusCode,i),!0}a(Rr,"sendMappedError");function _r(e){fe(e,502,{error:yt("upstream_error"),code:"upstream_error"})}a(_r,"sendUpstreamError");function Lr(e,t="rest"){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){fe(r,400,{error:yt("missing_flow_id"),code:"missing_flow_id"});return}let s=Wi(_(n.query,"summary"));try{if(s){fe(r,200,await xr(e,o));return}fe(r,200,await xe(e,o))}catch(i){if(Rr(r,i,t))return;_r(r)}}}a(Lr,"getFlowOrSummaryHandler");function Cr(e,t="rest"){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||!o){fe(r,400,{error:yt("missing_node_id"),code:"missing_node_id"});return}let s=_(n.query,"flow");try{fe(r,200,await Sr(e,o,s))}catch(i){if(Rr(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;_r(r)}}}a(Cr,"getNodeHandler");function Er(e,t="rest"){return async(n,r)=>{let o=n.params.id,s=n.params.name;if(typeof o!="string"||!o){fe(r,400,{error:yt("missing_node_id"),code:"missing_node_id"});return}if(typeof s!="string"||!s){fe(r,400,{error:yt("missing_field"),code:"missing_field"});return}let i=_(n.query,"flow"),d=_(n.query,"lines");try{fe(r,200,await Tr(e,o,s,{...i?{flowId:i}:{},...d?{lines:d}:{}}))}catch(l){if(Rr(r,l,t,"Use summary/search to discover node ids and fields."))return;_r(r)}}}a(Er,"getNodeFieldHandler");function Ir(e,t="rest"){return async(n,r)=>{let o=_(n.query,"q");if(!o){fe(r,400,{error:yt("missing_query"),code:"missing_query",hint:t==="get"?"Example: GET \u2026/search?q=TODO&flow=tab1":se("missing_query",t)});return}let s=_(n.query,"flow");try{fe(r,200,await Pr(e,o,s?{flowId:s}:{}))}catch(i){if(Rr(r,i,t))return;_r(r)}}}a(Ir,"searchFlowsHandler");K();ut();var zu={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 En(e){return zu[e]??"Request failed"}a(En,"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 Wo(e,t){let n=O(t);if(!n)return!1;let r=n.body.code,o={error:En(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(Wo,"sendMappedError");function Nr(e){return async(t,n)=>{try{let r=_(t.query,"q"),o=await ir(e.nodeTypes,r?{q:r}:{});et(n,200,o)}catch(r){if(Wo(n,r))return;et(n,502,{error:En("upstream_error"),code:"upstream_error"})}}}a(Nr,"listNodeTypesHandler");function Fr(e){return async(t,n)=>{try{let r=t.params.type,o=typeof r=="string"?decodeURIComponent(r):"",s=_(t.query,"help"),i=!(s==="0"||s==="false"||s==="no"||s==="omit"),d=await ct(e.nodeTypes,o,{includeHelp:i});et(n,200,d)}catch(r){if(Wo(n,r))return;et(n,502,{error:En("upstream_error"),code:"upstream_error"})}}}a(Fr,"getNodeTypeHandler");function Or(e){return async(t,n)=>{try{let r=_(t.query,"q");if(!r){et(n,400,{error:En("missing_query"),code:"missing_query",hint:"Pass ?q= to search the Node-RED catalogue (recommend only)."});return}let o=await ar(e.nodeTypes,r,{...e.catalogueFetcher?{catalogueFetcher:e.catalogueFetcher}:{},...e.catalogueUrl?{catalogueUrl:e.catalogueUrl}:{}});et(n,200,o)}catch(r){if(Wo(n,r))return;et(n,502,{error:En("upstream_error"),code:"upstream_error"})}}}a(Or,"searchPaletteHandler");Xe();ve();function tn(e,t){return e[t]??""}a(tn,"lineAt");function In(e,t,n){let r=e[t];return r?r[n]??0:0}a(In,"cell");function zi(e,t,n,r){let o=e[t];o&&(o[n]=r)}a(zi,"setCell");function Ji(e,t){let n=Gi(e),r=Gi(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)tn(n,u)===tn(r,p)?zi(i,u,p,In(i,u+1,p+1)+1):zi(i,u,p,Math.max(In(i,u+1,p),In(i,u,p+1)));let d=[],l=0,c=0;for(;l<o&&c<s;){let u=tn(n,l),p=tn(r,c);u===p?(d.push({op:"equal",text:u}),l+=1,c+=1):In(i,l+1,c)>=In(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:tn(n,l)}),l+=1;for(;c<s;)d.push({op:"add",text:tn(r,c)}),c+=1;return d}a(Ji,"diffLines");function Gi(e){return e.length===0?[]:e.split(`
953
- `)}a(Gi,"splitLines");Ae();var Xi=new Set(["func","template","info","payload","code","text","format","syntax"]),Gu=new Set(["x","y","w","h","wires","g"]),Ju=new Set(["w","h","g"]);function wt(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(wt,"nodeName");function Ki(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(Ki,"collectNodes");function Dr(e){if(e===void 0)return"undefined";if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(Dr).join(",")}]`;let t=e;return`{${Object.keys(t).sort().map(r=>`${JSON.stringify(r)}:${Dr(t[r])}`).join(",")}}`}a(Dr,"stableStringify");function Yi(e,t){return Dr(e)===Dr(t)}a(Yi,"valuesEqual");function jr(e){return typeof e=="string"?e:null}a(jr,"asText");function Ku(e){return/<\/?[a-z][\s\S]*>/i.test(e.slice(0,800))}a(Ku,"looksLikeHtml");function Vu(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:Ku(n)}a(Vu,"isHtmlTemplateField");function Qi(e,t,n){let r=jr(n);if(Xi.has(t)&&r!==null){let o={key:t,kind:"text",value:r};return Vu(e,t,r)&&(o.preview="html"),o}return{key:t,kind:"scalar",value:n===void 0?null:n}}a(Qi,"toPresenceProperty");function Xu(e,t){let n=[];for(let r of[...Object.keys(e)].sort())if(!(Ju.has(r)||r==="id"||r==="z")){if(r==="wires"){let o=Zi(e,t);n.push({key:"wires",kind:"scalar",value:o.length>0?o:e.wires??null});continue}n.push(Qi(e,r,e[r]))}return n}a(Xu,"nodeProperties");function Zi(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?wt(l)??l.type:d})}r.push({port:s,to:i})}),r}a(Zi,"describeWiresStructured");var Yu=new Set(["func","template","code","text"]);function Qu(e,t){let n=[],r=wt(e);r&&n.push({key:"name",kind:"scalar",value:r});let o=Zi(e,t);o.length>0&&n.push({key:"wires",kind:"scalar",value:o});for(let s of Yu){if(!(s in e))continue;let i=jr(e[s]);i===null||i.length===0||n.push(Qi(e,s,i))}return n}a(Qu,"buildKeyFields");function Zu(e,t,n){let r=Ve(e.type),o=wt(e),s=o?` "${o}"`:"",i=t?` in "${t}"`:"",d=n===1?"1 field":`${n} fields`;return`Changes ${r} node${s}${i} (${d})`}a(Zu,"buildModifiedSummary");function ep(e,t,n){let r=Ve(e.type),o=wt(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(ep,"buildPresenceSummary");function $r(e,t,n,r){let o={id:e.id,type:e.type,summary:ep(e,t,n),keyFields:Qu(e,r),properties:Xu(e,r)},s=wt(e);return s!==void 0&&(o.name=s),o}a($r,"toPresenceNode");function tp(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let o of[...n].sort()){if(Gu.has(o)||o==="id"||o==="z")continue;let s=e[o],i=t[o];if(Yi(s,i))continue;let d=jr(s),l=jr(i);if(Xi.has(o)&&(d!==null||l!==null)){let c=d??"",u=l??"";r.push({key:o,kind:"text",before:c,after:u,lines:Ji(c,u)});continue}r.push({key:o,kind:"scalar",before:s===void 0?null:s,after:i===void 0?null:i})}return r}a(tp,"compareNodes");function np(e,t){let n=["label","disabled","info","env"],r=[];for(let o of n)Yi(e[o],t[o])||r.push(o);return r}a(np,"compareTabMeta");function Mr(e){return typeof e.label=="string"&&e.label.trim()?e.label.trim():""}a(Mr,"flowLabelOf");function ea(e,t){let n=Ki(t),r=Ki(e),o=Mr(e)||Mr(t),s=Mr(t)||Mr(e),i=[],d=[],l=[];for(let c of e.nodes??[])n.has(c.id)||i.push($r(c,"added",o,r));for(let c of e.configs??[])n.has(c.id)||i.push($r(c,"added",o,r));for(let c of t.nodes??[])r.has(c.id)||d.push($r(c,"removed",s,n));for(let c of t.configs??[])r.has(c.id)||d.push($r(c,"removed",s,n));for(let[c,u]of r){let p=n.get(c);if(!p)continue;let f=tp(p,u);if(f.length>0){let m={id:c,type:u.type||p.type,summary:Zu(u,o||s,f.length),changes:f},h=wt(u)??wt(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:np(t,e),beforeNodeCount:n.size,afterNodeCount:r.size},added:i,removed:d,modified:l}}a(ea,"buildDiffSections");function Vi(e,t,n={}){let r=ea(e.payload.flow,t.flow),o=n.currentRev??t.rev,s=e.expectedRev,i=n.baseRevStale??(e.status==="pending"&&s!==o),d={proposalId:e.id,flowId:e.payload.flowId,expectedRev:s,currentRev:o,baseRevStale:i,compareAgainst:n.compareAgainst??"current",...r};return e.warnings&&e.warnings.length>0&&(d.warnings=e.warnings.map(l=>({...l}))),n.baseSnapshotMissing&&(d.baseSnapshotMissing=!0),n.vsCurrent&&(d.vsCurrent=n.vsCurrent),d}a(Vi,"buildProposalDiff");async function nn(e,t){let n=e.payload.flowId,r={id:n,type:"tab",nodes:[],configs:[]},o=e.expectedRev,s=r;try{let c=await t.getFlow(n);o=c.rev,s=c.flow}catch(c){if(!(c instanceof z))throw c;try{o=(await t.getFlows()).rev}catch{}}let d=e.status==="pending"&&e.expectedRev!==o,l=e.baseFlow;if(l!==void 0){let c=d?ea(e.payload.flow,s):void 0;return Vi(e,{rev:e.expectedRev,flow:l},{currentRev:o,compareAgainst:"base",baseRevStale:d,...c?{vsCurrent:c}:{}})}return Vi(e,{rev:o,flow:s},{compareAgainst:"current",baseSnapshotMissing:!0,baseRevStale:d})}a(nn,"computeProposalDiff");Ae();_e();ye();var ta=require("path");function Hr(e){return(0,ta.join)(e.userDir,".ai-access")}a(Hr,"getStorageDir");var rp=["status:read","flows:read","proposals:create","proposals:read-own"];function zo(){return Vt()}a(zo,"readPackageVersion");function op(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/1e3)):0}a(op,"secondsRemainingUntil");function sp(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/6e4)):0}a(sp,"minutesRemainingUntil");function ip(e,t){let n=de(t),r=qe(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(ip,"baseUrlsForTokenKind");function rn(e,t){let n=Kn(e,t),r=he(e.settings);if(t.auth){let o=t.auth.extra?.user;return{ok:!0,version:zo(),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:zo(),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:sp(t.accessLink.expiresAt,o),secondsRemaining:op(t.accessLink.expiresAt,o),permissions:[...rp],baseUrls:ip(t.accessLink.kind,e.settings.httpAdminRoot)}}}return{ok:!0,version:zo(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:n.permission,capabilities:n.capabilities,storageDir:Hr(e.settings),defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,maxActiveLinks:r.maxActiveLinks,defaultDeploymentType:r.defaultDeploymentType,caller:{kind:"admin",...n.permission?{permission:n.permission}:{}}}}a(rn,"buildStatus");function na(e,t){let n=t.draftStore??new br,r=t.rateLimiters??Ci(t.rateLimit??e.settings.aiAccess),o=e.httpAdmin,s="/mcp/a/:token",i=[r.ip,Ii(e,t.linkStore),r.token];o.use("/mcp/a",gi()),o.get(s,...i,R((u,p)=>{F(p),p.setHeader("X-Content-Type-Options","nosniff");let f=de(e.settings.httpAdminRoot),m=u.accessLink;if(!m){C(p,401,"Access link required","access_link_missing");return}let h=Si({link:m,openapiPath:`${f}/openapi.yaml`,restApiBase:f,mcpRpcPath:qe(e.settings.httpAdminRoot),publicOrigin:oo(u)});p.status(200).type("text/markdown; charset=utf-8").send(h)})),o.get(`${s}/status`,...i,R((u,p)=>{F(p),p.json(rn(e,u))})),o.get(`${s}/flows`,...i,R(async(u,p)=>{F(p);try{p.json(await pe(t.flowAdapter))}catch(f){let m=O(f);if(m){p.status(m.statusCode).json({...m.body,hint:"Retry after the owner fixes the plugin runtime."});return}C(p,502,f instanceof Error?f.message:String(f),"upstream_error")}})),o.get(`${s}/flows/:id`,...i,R(async(u,p)=>{F(p),await Lr(t.flowAdapter,"get")(u,p)})),o.get(`${s}/nodes/:id`,...i,R(async(u,p)=>{F(p),await Cr(t.flowAdapter,"get")(u,p)})),o.get(`${s}/nodes/:id/field/:name`,...i,R(async(u,p)=>{F(p),await Er(t.flowAdapter,"get")(u,p)})),o.get(`${s}/search`,...i,R(async(u,p)=>{F(p),await Ir(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,R(async(u,p)=>{F(p),await Nr(d)(u,p)})),o.get(`${s}/node-types/:type`,...i,R(async(u,p)=>{F(p),await Fr(d)(u,p)})),o.get(`${s}/palette/search`,...i,R(async(u,p)=>{F(p),await Or(d)(u,p)}));let l=e.settings.httpAdminRoot;ap(o,s,i,t,l),o.get(`${s}/propose`,...i,R(ji(t.proposalStore,t.auditLog,t.flowAdapter,l,t.nodeTypesAdapter))),cp(o,s,i,n,t,l);let c=Fi();return o.all(`${s}/links`,c),o.all(`${s}/links/:id`,c),o.use("/mcp/a",Ni()),n}a(na,"registerGetLinkRoutes");function ap(e,t,n,r,o){e.get(`${t}/proposals`,...n,R(async(s,i)=>{F(i);let d=s.accessLink;if(!d){C(i,401,"Access link required","access_link_missing");return}let l=q(s,o),c=re(s.query),u=mr(s.query),p;try{p=(await pe(r.flowAdapter)).rev}catch{}let f=p?V(r.proposalStore,p):0,m=qt(r.proposalStore,d,l,u);i.json({proposals:m.proposals.map(h=>ue(r.proposalStore,d,h,{...l,include:c},{includeConflicts:!1})),...ft({...m,returned:m.proposals.length}),...Q(f)})})),e.get(`${t}/proposals/:id`,...n,R(async(s,i)=>{F(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){C(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){C(i,401,"Access link required","access_link_missing");return}try{let c=q(s,o),u=re(s.query),p;try{p=(await pe(r.flowAdapter)).rev}catch{}let f=De(r.proposalStore,l,d,c);i.json(ue(r.proposalStore,l,f,{...c,include:u},{...p?{liveRev:p}:{}}))}catch(c){let u=O(c,"get");if(u){i.status(u.statusCode).json({...u.body,hint:u.body.hint??"List proposals with GET \u2026/proposals."});return}$(i,c);return}})),dp(e,t,n,r,o),lp(e,t,n,r,o)}a(ap,"registerGetLinkProposalRoutes");function dp(e,t,n,r,o){e.get(`${t}/proposals/:id/withdraw`,...n,R((s,i)=>{F(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){C(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){C(i,401,"Access link required","access_link_missing");return}let c=s.query.confirm;if(c!=="1"&&c!=="true"){C(i,400,"Withdraw over GET requires confirm=1","confirm_required",se("confirm_required","get"));return}let u=typeof s.query.reason=="string"?s.query.reason:void 0,p=q(s,o),f=re(s.query);try{let m=Wt(r.proposalStore,r.auditLog,l,d,u,p);i.json(ue(r.proposalStore,l,m,{...p,include:f}))}catch(m){let h=O(m,"get");if(h){let g=h.body.code;C(i,h.statusCode,Oe[g]??"Request failed",g,se(g,"get"));return}C(i,500,"Internal server error","internal_error");return}}))}a(dp,"registerGetLinkWithdrawRoute");function lp(e,t,n,r,o){e.get(`${t}/proposals/:id/diff`,...n,R(async(s,i)=>{F(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){C(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){C(i,401,"Access link required","access_link_missing");return}try{let c=De(r.proposalStore,l,d,q(s,o)),u=_(s.query,"format"),p=Yt(u==="json"||u==="md"?void 0:u);if(!p.ok){C(i,400,"Invalid diff format","invalid_diff_format","Valid formats: summary, full (default summary).");return}i.json(Qt(await nn(c,r.flowAdapter),p.format))}catch(c){let u=O(c);if(u){i.status(u.statusCode).json(u.body);return}$(i,c);return}}))}a(lp,"registerGetLinkDiffRoute");function cp(e,t,n,r,o,s){e.get(`${t}/draft/new`,...n,R($i(r,o.flowAdapter))),e.get(`${t}/draft/:draftId/append`,...n,R(Mi(r))),e.get(`${t}/draft/:draftId/submit`,...n,R(Di(r,o.proposalStore,o.auditLog,o.flowAdapter,s,o.nodeTypesAdapter)))}a(cp,"registerGetLinkDraftRoutes");G();var ra=require("module");function up(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,ra.createRequire)(e)}a(up,"createPluginRequire");function pp(e){try{let r=up()("@node-red/runtime")._?.adminApi?.httpAdmin;if(r&&typeof r.use=="function"&&r!==e.httpAdmin)return r}catch{}return null}a(pp,"resolveEditorHttpAdmin");function oa(e){let t=[e.httpAdmin],n=pp(e);return n&&t.push(n),t}a(oa,"httpAdminAppsForMcpErrors");var sa=Symbol.for("com55.ai-access.mcpJsonErrorHandler");function fp(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(fp,"mcpRpcMatcher");function Go(e){return e.includes("/mcp")}a(Go,"isMcpPath");function mp(e){return e.path||e.url||e.originalUrl||""}a(mp,"requestPath");function hp(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(hp,"isJsonParseError");function gp(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(gp,"isPayloadTooLarge");function yp(e){let t=fp(e);return(n,r,o,s)=>{if(o.headersSent){s(n);return}let i=mp(r);if(hp(n)&&Go(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(gp(n)&&Go(i)){o.status(413).json({error:`Request body exceeds limit of ${j} bytes`,code:"payload_too_large"});return}if(Go(i)){if(t(i)){o.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:wr(n).error,data:{code:"internal_error"}},id:null});return}o.status(500).json(wr(n));return}s(n)}}a(yp,"mcpJsonErrorHandler");function wp(e,t){let n=e;n[sa]||(n[sa]=!0,e.use(yp(t)))}a(wp,"attachOnce");function ia(e){for(let t of oa(e))wp(t,e.settings.httpAdminRoot)}a(ia,"attachMcpJsonErrorHandlers");Ln();var bt=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"}},vt=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"}},Nn=class extends Error{static{a(this,"AccessLinkValidationError")}code="access_link_invalid";statusCode=400;constructor(t){super(t),this.name="AccessLinkValidationError"}},tt=class extends Error{static{a(this,"AccessLinkTtlError")}code="invalid_ttl";statusCode=400;hint=mt;constructor(t){super(t),this.name="AccessLinkTtlError"}};var kt=require("crypto");Ln();function Jo(e){return e==="get_link"||e==="rest"||e==="mcp"}a(Jo,"isAccessLinkKind");function Ko(e){return e??"get_link"}a(Ko,"resolveAccessLinkKind");function aa(e){return(0,kt.createHash)("sha256").update(e,"utf8").digest("hex")}a(aa,"hashToken");function bp(e,t){let n=Buffer.from(e,"utf8"),r=Buffer.from(t,"utf8");return n.length!==r.length?!1:(0,kt.timingSafeEqual)(n,r)}a(bp,"hashesEqual");function Fn(e){let t={id:e.id,kind:Ko(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(Fn,"toPublic");var At=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,kt.randomBytes)(12).toString("hex")),this.randomToken=t.randomToken??(()=>`aia_${(0,kt.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(Fn)}create(t={}){let n=this.now();if([...this.links.values()].filter(p=>this.isActive(p,n)).length>=this.maxActiveLinks)throw new bt(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 tt("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=Ko(t.kind),d=No(i);if(o>d)throw new tt(Fo(i,d));let l=this.randomId(),c=this.randomToken(),u={id:l,tokenHash:aa(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),{...Fn(u),token:c}}revoke(t){let n=this.links.get(t);if(!n)throw new vt(t);return n.revokedAtMs??=this.now(),Fn(n)}getById(t){if(typeof t!="string"||t.length===0)return null;let n=this.links.get(t);return n?Fn(n):null}validateResult(t){if(typeof t!="string"||t.length===0)return{status:"invalid"};let n=aa(t),r=this.now();for(let o of this.links.values())if(bp(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:Fn(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)}};Ln();ie();ye();function Vo(e,t){return t}a(Vo,"storeForRequest");function la(e){let t=typeof e.user?.username=="string"?e.user.username:void 0;return t?{kind:"admin",username:t}:{kind:"admin"}}a(la,"adminActor");function Ur(e){let t=he(e.settings);return new At({defaultTtlSeconds:t.defaultLinkTtlSeconds,maxActiveLinks:t.maxActiveLinks})}a(Ur,"createConfiguredLinkStore");function ca(e){e.setHeader("Cache-Control","no-store"),e.setHeader("Pragma","no-cache")}a(ca,"noStore");function ua(e,t){return(n,r)=>{let s=Vo(e,t).list();ca(r),r.json({links:s})}}a(ua,"listLinksHandler");function pa(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:mt});return}i.ttlSeconds=s.ttlSeconds}if(typeof s.label=="string"&&(i.label=s.label),s.kind!==void 0){if(!Jo(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=Vo(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",la(r),{linkId:d.id,label:d.label,expiresAt:d.expiresAt,createdAt:d.createdAt,kind:l,...p!==void 0?{ttlSeconds:p}:{}})}if(ca(o),l==="get_link"){let c=Ps(r,e.settings.httpAdminRoot,d.token);o.status(201).json({...d,url:c});return}o.status(201).json({...d})}catch(d){if(d instanceof bt){o.status(d.statusCode).json({error:d.message,code:d.code});return}if(d instanceof tt){o.status(d.statusCode).json({error:d.message,code:d.code,hint:d.hint});return}if(d instanceof Nn){o.status(d.statusCode).json({error:d.message,code:d.code});return}$(o,d);return}}}a(pa,"createLinkHandler");function fa(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=Vo(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",la(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 vt){o.status(i.statusCode).json({error:i.message,code:i.code});return}$(o,i);return}}}a(fa,"revokeLinkHandler");function vp(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(vp,"ttlSecondsFromLink");function kp(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=vp(l);c!==void 0&&(n.ttlSeconds=c)}return{...e,details:n}}a(kp,"enrichAuditLogEntry");function ma(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=>kp(d,t));r.json({logs:i})}}a(ma,"listLogsHandler");var Xo=require("@modelcontextprotocol/sdk/server/mcp.js"),ha=require("@modelcontextprotocol/sdk/server/streamableHttp.js"),T=require("zod");Ge();G();it();K();Fe();ut();Xe();Ae();function ga(){return"0.2.0-beta.4"}a(ga,"pluginVersion");async function ya(e){try{return(await pe(e)).rev}catch{return}}a(ya,"tryLiveRev");function Z(e){return{content:[{type:"text",text:JSON.stringify(e)}]}}a(Z,"jsonResult");function Yo(e){return e===!0?new Set(["payload","baseFlow"]):new Set}a(Yo,"includeSetFromPayloadFlag");function ee(e,t=500){let n=_n(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(ee,"jsonError");var Ap=new Set(["list_proposals","list_my_proposals"]),xp=new Set(["status","limit","offset","mine","flowId"]);function Sp(e){return!!e&&typeof e=="object"&&typeof e.safeParseAsync=="function"}a(Sp,"isZodLikeSchema");function Tp(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(Ap.has(e)&&xp.has(s))return Bt(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 y(d,"invalid_arguments",{details:{parameter:s,errors:i,hint:`Fix '${s}' to match the ${e} input schema (see tools/list).`}})}a(Tp,"mcpArgumentError");function wa(e){let t=e,n=t.validateToolInput;typeof n=="function"&&(t.validateToolInput=async(r,o,s)=>{let i=r.inputSchema;if(!Sp(i))return n.call(e,r,o,s);let d=await i.safeParseAsync(o??{});if(d.success)return d.data;let l=_n(Tp(s,o,d.error.issues),"mcp"),c=l?{...l.body,statusCode:l.statusCode}:{};throw new Error(JSON.stringify(c))})}a(wa,"installMcpArgumentErrorMapping");function ba(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 Z(rn(t,r))}catch(o){return ee(o)}}),e.registerTool("list_flows",{description:"List all flows as a flat Admin API v2 array plus the current rev string. Returns { rev, flows }. Call this (or get_flow) before submit_proposal so expectedRev matches."},async()=>{try{return Z(await pe(n.flowAdapter))}catch(o){return ee(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:T.z.string().min(1).describe("Flow / tab id"),summary:T.z.boolean().optional().describe("When true, return node list with sizes only (no field contents)")}},async({id:o,summary:s})=>{try{return Z(s?await xr(n.flowAdapter,o):await xe(n.flowAdapter,o))}catch(i){return ee(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:T.z.string().min(1).describe("Node id"),flow:T.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,flow:s})=>{try{return Z(await Sr(n.flowAdapter,o,s))}catch(i){return ee(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:T.z.string().min(1).describe("Node id"),field:T.z.string().min(1).describe("Field name (e.g. template, func)"),lines:T.z.string().optional().describe("Optional 1-based line range A or A-B"),flow:T.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,field:s,lines:i,flow:d})=>{try{return Z(await Tr(n.flowAdapter,o,s,{...d?{flowId:d}:{},...i?{lines:i}:{}}))}catch(l){return ee(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:T.z.string().describe("Substring to find (case-sensitive; non-empty required)"),flow:T.z.string().min(1).optional().describe("Optional tab id")}},async({q:o,flow:s})=>{try{return Z(await Pr(n.flowAdapter,o,s?{flowId:s}:{}))}catch(i){return ee(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:T.z.string().optional().describe("Optional case-insensitive filter")}},async({q:o})=>{try{return Z(await ir(n.nodeTypesAdapter,o?{q:o}:{}))}catch(s){return ee(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:T.z.string().min(1).describe("Node type id (e.g. inject, template) \u2014 case-sensitive"),includeHelp:T.z.boolean().optional().describe("When false, omit help sections (default true)")}},async({type:o,includeHelp:s})=>{try{return Z(await ct(n.nodeTypesAdapter,o,{includeHelp:s!==!1}))}catch(i){return ee(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:T.z.string().describe("Search string (id / description / keywords; non-empty required)")}},async({q:o})=>{try{return Z(await ar(n.nodeTypesAdapter,o,{...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{}}))}catch(s){return ee(s,502)}})}a(ba,"registerReadTools");function va(e,t,n,r,o){let s=q(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. Payload size limit is ${j} bytes (413 if exceeded). expectedRev is checked against the live rev at submit time (409 if stale). 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:T.z.literal("replace_flow").describe("Must be 'replace_flow'"),expectedRev:T.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:T.z.string().min(1).optional().describe("Alias for expectedRev (snake_case); prefer expectedRev"),title:T.z.string().max(80).optional().describe("Optional short human title (\u226480 chars). Auto-generated from changes when omitted."),include_payload:T.z.boolean().optional().describe("When true, include payload and baseFlow in the response (default lean)"),nodes:T.z.array(T.z.record(T.z.string(),T.z.unknown())).optional().describe("Optional node ops (set/add/remove/replace_text). Same as payload.nodes when flow is omitted."),payload:T.z.object({flowId:T.z.string().min(1).describe("Tab id to replace"),flow:T.z.object({id:T.z.string().optional(),type:T.z.string().optional().describe("Usually 'tab' (included by get_flow)"),label:T.z.string().optional(),nodes:T.z.array(T.z.record(T.z.string(),T.z.unknown())).optional().describe("Child nodes for the tab (from get_flow.nodes)"),configs:T.z.array(T.z.record(T.z.string(),T.z.unknown())).optional()}).passthrough().optional().describe("Full tab from get_flow; omit when using nodes ops"),nodes:T.z.array(T.z.record(T.z.string(),T.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 Ye(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={...ce(d,{...s,include:Yo(i.include_payload)}),mine:!0},c=V(n.proposalStore,d.expectedRev,{flowId:d.payload.flowId}),u=Ne(Ie(n.proposalStore,n.proposalStore.get(d.id),o.id));return Z({...l,...u,...Q(c)})}catch(d){return ee(d)}}),Rp(e,n,o,s),_p(e,n,o,s)}a(va,"registerProposalTools");function Pp(e,t,n,r){let o={mine:T.z.boolean().optional().describe("true: only proposals created by this access link; false: only other links\u2019 proposals"),status:T.z.union([T.z.string(),T.z.array(T.z.string())]).optional().describe("Filter by proposal status: one value, an array, or a comma-separated string (pending, approved, rejected, failed, withdrawn, expired, superseded). An empty array or empty string means no status filter."),flowId:T.z.string().optional().describe("Filter by flow tab id (payload.flowId)"),limit:T.z.number().int().min(1).max(Ut).optional().describe(`Page size (1\u2013${Ut}). Omit to return all matches.`),offset:T.z.number().int().min(0).optional().describe("Skip this many filtered results (default 0)"),include_payload:T.z.boolean().optional().describe("When true, include payload and baseFlow on each proposal")},s=a(async i=>{try{let d=Yo(i.include_payload),l=await ya(t.flowAdapter),c=l?V(t.proposalStore,l):0,u={};typeof i.mine=="boolean"&&(u.mine=i.mine);let p=Io(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=qt(t.proposalStore,n,r,u);return Z({proposals:f.proposals.map(m=>ue(t.proposalStore,n,m,{...r,include:d},{includeConflicts:!1})),...ft({...f,returned:f.proposals.length}),...Q(c)})}catch(d){return ee(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(Pp,"registerListProposalsTools");function Rp(e,t,n,r){Pp(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 pending proposals, conflictingProposals when other pending work touches the same flow. Lean by default (no payload/baseFlow); pass include_payload=true for full bodies. Cite displayName + reviewUrl when talking to the user.",inputSchema:{id:T.z.string().min(1).describe("Proposal id (prop-\u2026)"),include_payload:T.z.boolean().optional().describe("When true, include payload and baseFlow")}},async({id:o,include_payload:s})=>{try{let i=await ya(t.flowAdapter),d=De(t.proposalStore,n,o,r);return Z(ue(t.proposalStore,n,d,{...r,include:Yo(s)},{...i?{liveRev:i}:{}}))}catch(i){return ee(i)}})}a(Rp,"registerProposalQueryTools");function _p(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). Pending + stale also includes vsCurrent.",inputSchema:{id:T.z.string().min(1).describe("Proposal id (prop-\u2026)"),format:T.z.enum(["summary","full"]).optional().describe("Diff payload format (default summary)")}},async({id:o,format:s})=>{try{let i=De(t.proposalStore,n,o),d=Yt(s??"summary");if(!d.ok)return ee(new y("Invalid diff format","invalid_diff_format",{details:{hint:"Valid formats: summary, full (default summary)."}}));let l=Qt(await nn(i,t.flowAdapter),d.format);return Z(l)}catch(i){return ee(i)}}),e.registerTool("withdraw_proposal",{description:"Withdraw one of YOUR pending proposals (status becomes withdrawn, not rejected). Optional reason. Only the creating access link can withdraw; other tokens get 403. Cite displayName + reviewUrl when telling the user.",inputSchema:{id:T.z.string().min(1).describe("Proposal id (prop-\u2026) owned by this access link"),reason:T.z.string().optional().describe("Optional withdraw reason")}},({id:o,reason:s})=>{try{let i=Wt(t.proposalStore,t.auditLog,n,o,s,r);return Z(ue(t.proposalStore,n,i,r))}catch(i){return ee(i)}})}a(_p,"registerProposalActionTools");function Lp(e,t,n){let r=new Xo.McpServer({name:"node-red-ai-access",version:ga()});wa(r);let o=n.accessLink;if(!o)throw new Error("Access link required");return ba(r,e,t,n),va(r,e,t,n,o),r}a(Lp,"createAiMcpServer");function Qo(e,t){return async(n,r,o)=>{try{if(!n.accessLink&&!n.auth){r.setHeader("WWW-Authenticate",Y),r.status(401).json({error:le,code:Le});return}let s=n.accessLink?Lp(e,t,n):Cp(e,t,n),i=new ha.StreamableHTTPServerTransport({enableJsonResponse:!0});await s.connect(i),await i.handleRequest(n,r,n.body),r.on("close",()=>{i.close(),s.close()})}catch(s){if(!r.headersSent){$(r,s);return}o(s instanceof Error?s:new Error(String(s)))}}}a(Qo,"mcpRpcPostHandler");function Cp(e,t,n){let r=new Xo.McpServer({name:"node-red-ai-access",version:ga()});if(wa(r),ba(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:lo(i,s),kind:"mcp",createdAt:new Date().toISOString(),expiresAt:new Date((n.auth?.expiresAt??0)*1e3).toISOString(),createdBy:s,oauthClientId:i,oauthUser:s};va(r,e,t,n,l)}return r}a(Cp,"createOAuthMcpServer");function On(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(On,"mcpRpcMethodNotAllowed");var $n=require("crypto"),sn=require("fs"),ts=require("path"),B=require("@modelcontextprotocol/sdk/server/auth/errors.js"),Ta=require("@modelcontextprotocol/sdk/server/auth/handlers/authorize.js"),ns=require("@modelcontextprotocol/sdk/server/auth/handlers/metadata.js"),Pa=require("@modelcontextprotocol/sdk/server/auth/handlers/register.js"),Ra=require("@modelcontextprotocol/sdk/server/auth/handlers/revoke.js"),_a=require("@modelcontextprotocol/sdk/server/auth/handlers/token.js"),La=require("@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js"),Ca=require("@modelcontextprotocol/sdk/server/auth/router.js"),Ea=ln(require("express"),1);G();_e();function $e(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t]??t)}a($e,"esc");var ka={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>'},Ep={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."]},Ip='<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>',Np=`
954
+ `);for(let m=0;m<f.length;m+=1){let g=(f[m]??"").indexOf(n);if(g>=0){let k=0;for(let T=0;T<m;T+=1)k+=(f[T]?.length??0)+1;u(m+1,k+g)}}}if(n.includes(`
955
+ `)){let f=0;for(;f<=i.length;){let m=i.indexOf(n,f);if(m<0)break;u(bf(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,ff),lineCount:p,snippet:wf(i,Math.max(0,c),n.length)})}return r.length>=o}a(hs,"searchInNode");function $a(e){return[...e.nodes??[],...e.configs??[]]}a($a,"nodePool");async function vf(e,t,n,r,o){let s=await _e(e,t);for(let i of $a(s.flow))if(hs(i,t,n,r,o))return{rev:s.rev,truncated:!0};return{rev:s.rev,truncated:!1}}a(vf,"searchOneFlow");async function kf(e,t,n,r){let o=await ye(e),s=new Set;for(let i of Fa(o.flows))if(i.id)try{let d=await _e(e,i.id);for(let l of $a(d.flow))if(s.add(l.id),hs(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(hs(d,l,t,n,r))return{rev:o.rev,truncated:!0}}return{rev:o.rev,truncated:!1}}a(kf,"searchAllFlows");async function Vr(e,t,n={}){if(typeof t!="string"||!t.trim())throw new y("q query parameter is required","missing_query");let r=typeof n.cap=="number"&&n.cap>0?Math.floor(n.cap):pf,o=[],s=n.flowId?await vf(e,n.flowId,t,o,r):await kf(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(Vr,"searchFlows");function Ma(e){if(e===void 0)return!1;let t=e.toLowerCase();return t==="1"||t==="true"||t==="yes"}a(Ma,"isTruthyQueryFlag");nt();function Af(e,t,n){switch(e){case"node_not_found":case"flow_not_found":case"field_not_found":return n??de(e,t);default:return de(e,t)}}a(Af,"layeredReadErrorHint");function Tt(e){return je[e]??"Request failed"}a(Tt,"safeErrorText");function we(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(we,"sendJson");function Yr(e,t,n,r){let o=M(t,n);if(!o)return!1;let s=o.body.code,i={error:Tt(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=Af(s,n,r);return d&&(i.hint=d),we(e,o.statusCode,i),!0}a(Yr,"sendMappedError");function Xr(e){we(e,502,{error:Tt("upstream_error"),code:"upstream_error"})}a(Xr,"sendUpstreamError");function Zr(e,t="rest"){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){we(r,400,{error:Tt("missing_flow_id"),code:"missing_flow_id"});return}let s=Ma(P(n.query,"summary"));try{if(s){we(r,200,await Gr(Me(n,e),o));return}we(r,200,await _e(Me(n,e),o))}catch(i){if(Yr(r,i,t))return;Xr(r)}}}a(Zr,"getFlowOrSummaryHandler");function Qr(e,t="rest"){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||!o){we(r,400,{error:Tt("missing_node_id"),code:"missing_node_id"});return}let s=P(n.query,"flow");try{we(r,200,await Kr(Me(n,e),o,s))}catch(i){if(Yr(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;Xr(r)}}}a(Qr,"getNodeHandler");function eo(e,t="rest"){return async(n,r)=>{let o=n.params.id,s=n.params.name;if(typeof o!="string"||!o){we(r,400,{error:Tt("missing_node_id"),code:"missing_node_id"});return}if(typeof s!="string"||!s){we(r,400,{error:Tt("missing_field"),code:"missing_field"});return}let i=P(n.query,"flow"),d=P(n.query,"lines");try{we(r,200,await Jr(Me(n,e),o,s,{...i?{flowId:i}:{},...d?{lines:d}:{}}))}catch(l){if(Yr(r,l,t,"Use summary/search to discover node ids and fields."))return;Xr(r)}}}a(eo,"getNodeFieldHandler");function to(e,t="rest"){return async(n,r)=>{let o=P(n.query,"q");if(!o){we(r,400,{error:Tt("missing_query"),code:"missing_query",hint:t==="get"?"Example: GET \u2026/search?q=TODO&flow=tab1":de("missing_query",t)});return}let s=P(n.query,"flow");try{we(r,200,await Vr(Me(n,e),o,s?{flowId:s}:{}))}catch(i){if(Yr(r,i,t))return;Xr(r)}}}a(to,"searchFlowsHandler");J();wt();var Sf={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 Wn(e){return Sf[e]??"Request failed"}a(Wn,"safeErrorText");function it(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(it,"sendJson");function gs(e,t){let n=M(t);if(!n)return!1;let r=n.body.code,o={error:Wn(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."),it(e,n.statusCode,o),!0}a(gs,"sendMappedError");function no(e){return async(t,n)=>{try{let r=P(t.query,"q"),o=await xr(e.nodeTypes,r?{q:r}:{});it(n,200,o)}catch(r){if(gs(n,r))return;it(n,502,{error:Wn("upstream_error"),code:"upstream_error"})}}}a(no,"listNodeTypesHandler");function ro(e){return async(t,n)=>{try{let r=t.params.type,o=typeof r=="string"?decodeURIComponent(r):"",s=P(t.query,"help"),i=!(s==="0"||s==="false"||s==="no"||s==="omit"),d=await yt(e.nodeTypes,o,{includeHelp:i});it(n,200,d)}catch(r){if(gs(n,r))return;it(n,502,{error:Wn("upstream_error"),code:"upstream_error"})}}}a(ro,"getNodeTypeHandler");function oo(e){return async(t,n)=>{try{let r=P(t.query,"q");if(!r){it(n,400,{error:Wn("missing_query"),code:"missing_query",hint:"Pass ?q= to search the Node-RED catalogue (recommend only)."});return}let o=await Tr(e.nodeTypes,r,{...e.catalogueFetcher?{catalogueFetcher:e.catalogueFetcher}:{},...e.catalogueUrl?{catalogueUrl:e.catalogueUrl}:{}});it(n,200,o)}catch(r){if(gs(n,r))return;it(n,502,{error:Wn("upstream_error"),code:"upstream_error"})}}}a(oo,"searchPaletteHandler");tt();xe();function cn(e,t){return e[t]??""}a(cn,"lineAt");function Gn(e,t,n){let r=e[t];return r?r[n]??0:0}a(Gn,"cell");function Da(e,t,n,r){let o=e[t];o&&(o[n]=r)}a(Da,"setCell");function Ha(e,t){let n=ja(e),r=ja(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)cn(n,u)===cn(r,p)?Da(i,u,p,Gn(i,u+1,p+1)+1):Da(i,u,p,Math.max(Gn(i,u+1,p),Gn(i,u,p+1)));let d=[],l=0,c=0;for(;l<o&&c<s;){let u=cn(n,l),p=cn(r,c);u===p?(d.push({op:"equal",text:u}),l+=1,c+=1):Gn(i,l+1,c)>=Gn(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:cn(n,l)}),l+=1;for(;c<s;)d.push({op:"add",text:cn(r,c)}),c+=1;return d}a(Ha,"diffLines");function ja(e){return e.length===0?[]:e.split(`
956
+ `)}a(ja,"splitLines");Re();var qa=new Set(["func","template","info","payload","code","text","format","syntax"]),xf=new Set(["x","y","w","h","wires","g"]),Tf=new Set(["w","h","g"]);function Rt(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(Rt,"nodeName");function Ua(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(Ua,"collectNodes");function ao(e){if(e===void 0)return"undefined";if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(ao).join(",")}]`;let t=e;return`{${Object.keys(t).sort().map(r=>`${JSON.stringify(r)}:${ao(t[r])}`).join(",")}}`}a(ao,"stableStringify");function za(e,t){return ao(e)===ao(t)}a(za,"valuesEqual");function lo(e){return typeof e=="string"?e:null}a(lo,"asText");function Rf(e){return/<\/?[a-z][\s\S]*>/i.test(e.slice(0,800))}a(Rf,"looksLikeHtml");function _f(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:Rf(n)}a(_f,"isHtmlTemplateField");function Wa(e,t,n){let r=lo(n);if(qa.has(t)&&r!==null){let o={key:t,kind:"text",value:r};return _f(e,t,r)&&(o.preview="html"),o}return{key:t,kind:"scalar",value:n===void 0?null:n}}a(Wa,"toPresenceProperty");function Pf(e,t){let n=[];for(let r of[...Object.keys(e)].sort())if(!(Tf.has(r)||r==="id"||r==="z")){if(r==="wires"){let o=Ga(e,t);n.push({key:"wires",kind:"scalar",value:o.length>0?o:e.wires??null});continue}n.push(Wa(e,r,e[r]))}return n}a(Pf,"nodeProperties");function Ga(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?Rt(l)??l.type:d})}r.push({port:s,to:i})}),r}a(Ga,"describeWiresStructured");var Ef=new Set(["func","template","code","text"]);function Cf(e,t){let n=[],r=Rt(e);r&&n.push({key:"name",kind:"scalar",value:r});let o=Ga(e,t);o.length>0&&n.push({key:"wires",kind:"scalar",value:o});for(let s of Ef){if(!(s in e))continue;let i=lo(e[s]);i===null||i.length===0||n.push(Wa(e,s,i))}return n}a(Cf,"buildKeyFields");function Lf(e,t,n){let r=et(e.type),o=Rt(e),s=o?` "${o}"`:"",i=t?` in "${t}"`:"",d=n===1?"1 field":`${n} fields`;return`Changes ${r} node${s}${i} (${d})`}a(Lf,"buildModifiedSummary");function If(e,t,n){let r=et(e.type),o=Rt(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(If,"buildPresenceSummary");function so(e,t,n,r){let o={id:e.id,type:e.type,summary:If(e,t,n),keyFields:Cf(e,r),properties:Pf(e,r)},s=Rt(e);return s!==void 0&&(o.name=s),o}a(so,"toPresenceNode");function Nf(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let o of[...n].sort()){if(xf.has(o)||o==="id"||o==="z")continue;let s=e[o],i=t[o];if(za(s,i))continue;let d=lo(s),l=lo(i);if(qa.has(o)&&(d!==null||l!==null)){let c=d??"",u=l??"";r.push({key:o,kind:"text",before:c,after:u,lines:Ha(c,u)});continue}r.push({key:o,kind:"scalar",before:s===void 0?null:s,after:i===void 0?null:i})}return r}a(Nf,"compareNodes");function Ff(e,t){let n=["label","disabled","info","env"],r=[];for(let o of n)za(e[o],t[o])||r.push(o);return r}a(Ff,"compareTabMeta");function io(e){return typeof e.label=="string"&&e.label.trim()?e.label.trim():""}a(io,"flowLabelOf");function Ka(e,t){let n=Ua(t),r=Ua(e),o=io(e)||io(t),s=io(t)||io(e),i=[],d=[],l=[];for(let c of e.nodes??[])n.has(c.id)||i.push(so(c,"added",o,r));for(let c of e.configs??[])n.has(c.id)||i.push(so(c,"added",o,r));for(let c of t.nodes??[])r.has(c.id)||d.push(so(c,"removed",s,n));for(let c of t.configs??[])r.has(c.id)||d.push(so(c,"removed",s,n));for(let[c,u]of r){let p=n.get(c);if(!p)continue;let f=Nf(p,u);if(f.length>0){let m={id:c,type:u.type||p.type,summary:Lf(u,o||s,f.length),changes:f},h=Rt(u)??Rt(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:Ff(t,e),beforeNodeCount:n.size,afterNodeCount:r.size},added:i,removed:d,modified:l}}a(Ka,"buildDiffSections");function Ba(e,t,n={}){let r=Ka(e.payload.flow,t.flow),o=n.currentRev??t.rev,s=e.expectedRev,i=n.baseRevStale??(e.status==="pending"&&s!==o),d={proposalId:e.id,flowId:e.payload.flowId,expectedRev:s,currentRev:o,baseRevStale:i,compareAgainst:n.compareAgainst??"current",...r};return e.warnings&&e.warnings.length>0&&(d.warnings=e.warnings.map(l=>({...l}))),n.baseSnapshotMissing&&(d.baseSnapshotMissing=!0),n.vsCurrent&&(d.vsCurrent=n.vsCurrent),d}a(Ba,"buildProposalDiff");async function un(e,t){let n=e.payload.flowId,r={id:n,type:"tab",nodes:[],configs:[]},o=e.expectedRev,s=r;try{let c=await t.getFlow(n);o=c.rev,s=c.flow}catch(c){if(!(c instanceof W))throw c;try{o=(await t.getFlows()).rev}catch{}}let d=e.status==="pending"&&e.expectedRev!==o,l=e.baseFlow;if(l!==void 0){let c=d?Ka(e.payload.flow,s):void 0;return Ba(e,{rev:e.expectedRev,flow:l},{currentRev:o,compareAgainst:"base",baseRevStale:d,...c?{vsCurrent:c}:{}})}return Ba(e,{rev:o,flow:s},{compareAgainst:"current",baseSnapshotMissing:!0,baseRevStale:d})}a(un,"computeProposalDiff");Re();Le();ue();var Ja=require("path");function co(e){return(0,Ja.join)(e.userDir,".ai-access")}a(co,"getStorageDir");var Of=["status:read","flows:read","proposals:create","proposals:read-own"];function ys(){return rn()}a(ys,"readPackageVersion");function $f(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/1e3)):0}a($f,"secondsRemainingUntil");function Mf(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/6e4)):0}a(Mf,"minutesRemainingUntil");function Df(e,t){let n=fe(t),r=Ve(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(Df,"baseUrlsForTokenKind");function pn(e,t){let n=ur(e,t),r=ve(e.settings);if(t.auth){let o=t.auth.extra?.user;return{ok:!0,version:ys(),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:ys(),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:Mf(t.accessLink.expiresAt,o),secondsRemaining:$f(t.accessLink.expiresAt,o),permissions:[...Of],baseUrls:Df(t.accessLink.kind,e.settings.httpAdminRoot)}}}return{ok:!0,version:ys(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:n.permission,capabilities:n.capabilities,storageDir:co(e.settings),defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,maxActiveLinks:r.maxActiveLinks,defaultDeploymentType:r.defaultDeploymentType,caller:{kind:"admin",...n.permission?{permission:n.permission}:{}}}}a(pn,"buildStatus");function Va(e,t){let n=t.draftStore??new Br,r=t.rateLimiters??Sa(t.rateLimit??e.settings.aiAccess),o=e.httpAdmin,s="/mcp/a/:token",i=[r.ip,Ta(e,t.linkStore),r.token];o.use("/mcp/a",la()),o.get(s,...i,_((u,p)=>{O(p),p.setHeader("X-Content-Type-Options","nosniff");let f=fe(e.settings.httpAdminRoot),m=u.accessLink;if(!m){I(p,401,"Access link required","access_link_missing");return}let h=ya({link:m,openapiPath:`${f}/openapi.yaml`,restApiBase:f,mcpRpcPath:Ve(e.settings.httpAdminRoot),publicOrigin:_o(u)});p.status(200).type("text/markdown; charset=utf-8").send(h)})),o.get(`${s}/status`,...i,_((u,p)=>{O(p),p.json(pn(e,u))})),o.get(`${s}/flows`,...i,_(async(u,p)=>{O(p);try{p.json(await ye(t.flowAdapter))}catch(f){let m=M(f);if(m){p.status(m.statusCode).json({...m.body,hint:"Retry after the owner fixes the plugin runtime."});return}I(p,502,f instanceof Error?f.message:String(f),"upstream_error")}})),o.get(`${s}/flows/:id`,...i,_(async(u,p)=>{O(p),await Zr(t.flowAdapter,"get")(u,p)})),o.get(`${s}/nodes/:id`,...i,_(async(u,p)=>{O(p),await Qr(t.flowAdapter,"get")(u,p)})),o.get(`${s}/nodes/:id/field/:name`,...i,_(async(u,p)=>{O(p),await eo(t.flowAdapter,"get")(u,p)})),o.get(`${s}/search`,...i,_(async(u,p)=>{O(p),await to(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)=>{O(p),await no(d)(u,p)})),o.get(`${s}/node-types/:type`,...i,_(async(u,p)=>{O(p),await ro(d)(u,p)})),o.get(`${s}/palette/search`,...i,_(async(u,p)=>{O(p),await oo(d)(u,p)}));let l=e.settings.httpAdminRoot;jf(o,s,i,t,l),o.get(`${s}/propose`,...i,_(Ia(t.proposalStore,t.auditLog,t.flowAdapter,l,t.nodeTypesAdapter))),Bf(o,s,i,n,t,l);let c=_a();return o.all(`${s}/links`,c),o.all(`${s}/links/:id`,c),o.use("/mcp/a",Ra()),n}a(Va,"registerGetLinkRoutes");function jf(e,t,n,r,o){e.get(`${t}/proposals`,...n,_(async(s,i)=>{O(i);let d=s.accessLink;if(!d){I(i,401,"Access link required","access_link_missing");return}let l=q(s,o),c=ie(s.query),u=Mr(s.query),p;try{p=(await ye(r.flowAdapter)).rev}catch{}let f=p?V(r.proposalStore,p):0,m=Zt(r.proposalStore,d,l,u);i.json({proposals:m.proposals.map(h=>ge(r.proposalStore,d,h,{...l,include:c},{includeConflicts:!1})),...kt({...m,returned:m.proposals.length}),...Z(f)})})),e.get(`${t}/proposals/:id`,...n,_(async(s,i)=>{O(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){I(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){I(i,401,"Access link required","access_link_missing");return}try{let c=q(s,o),u=ie(s.query),p;try{p=(await ye(r.flowAdapter)).rev}catch{}let f=ze(r.proposalStore,l,d,c);i.json(ge(r.proposalStore,l,f,{...c,include:u},{...p?{liveRev:p}:{}}))}catch(c){let u=M(c,"get");if(u){i.status(u.statusCode).json({...u.body,hint:u.body.hint??"List proposals with GET \u2026/proposals."});return}D(i,c);return}})),Hf(e,t,n,r,o),Uf(e,t,n,r,o)}a(jf,"registerGetLinkProposalRoutes");function Hf(e,t,n,r,o){e.get(`${t}/proposals/:id/withdraw`,...n,_((s,i)=>{O(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){I(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){I(i,401,"Access link required","access_link_missing");return}let c=s.query.confirm;if(c!=="1"&&c!=="true"){I(i,400,"Withdraw over GET requires confirm=1","confirm_required",de("confirm_required","get"));return}let u=typeof s.query.reason=="string"?s.query.reason:void 0,p=q(s,o),f=ie(s.query);try{let m=Qt(r.proposalStore,r.auditLog,l,d,u,p);i.json(ge(r.proposalStore,l,m,{...p,include:f}))}catch(m){let h=M(m,"get");if(h){let g=h.body.code;I(i,h.statusCode,je[g]??"Request failed",g,de(g,"get"));return}I(i,500,"Internal server error","internal_error");return}}))}a(Hf,"registerGetLinkWithdrawRoute");function Uf(e,t,n,r,o){e.get(`${t}/proposals/:id/diff`,...n,_(async(s,i)=>{O(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){I(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){I(i,401,"Access link required","access_link_missing");return}try{let c=ze(r.proposalStore,l,d,q(s,o)),u=P(s.query,"format"),p=sn(u==="json"||u==="md"?void 0:u);if(!p.ok){I(i,400,"Invalid diff format","invalid_diff_format","Valid formats: summary, full (default summary).");return}i.json(an(await un(c,r.flowAdapter),p.format))}catch(c){let u=M(c);if(u){i.status(u.statusCode).json(u.body);return}D(i,c);return}}))}a(Uf,"registerGetLinkDiffRoute");function Bf(e,t,n,r,o,s){e.get(`${t}/draft/new`,...n,_(Ea(r,o.flowAdapter))),e.get(`${t}/draft/:draftId/append`,...n,_(Ca(r))),e.get(`${t}/draft/:draftId/submit`,...n,_(La(r,o.proposalStore,o.auditLog,o.flowAdapter,s,o.nodeTypesAdapter)))}a(Bf,"registerGetLinkDraftRoutes");G();var Ya=require("module");function qf(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Ya.createRequire)(e)}a(qf,"createPluginRequire");function zf(e){try{let r=qf()("@node-red/runtime")._?.adminApi?.httpAdmin;if(r&&typeof r.use=="function"&&r!==e.httpAdmin)return r}catch{}return null}a(zf,"resolveEditorHttpAdmin");function Xa(e){let t=[e.httpAdmin],n=zf(e);return n&&t.push(n),t}a(Xa,"httpAdminAppsForMcpErrors");var Za=Symbol.for("com55.ai-access.mcpJsonErrorHandler");function Wf(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(Wf,"mcpRpcMatcher");function ws(e){return e.includes("/mcp")}a(ws,"isMcpPath");function Gf(e){return e.path||e.url||e.originalUrl||""}a(Gf,"requestPath");function Kf(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(Kf,"isJsonParseError");function Jf(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(Jf,"isPayloadTooLarge");function Vf(e){let t=Wf(e);return(n,r,o,s)=>{if(o.headersSent){s(n);return}let i=Gf(r);if(Kf(n)&&ws(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(Jf(n)&&ws(i)){o.status(413).json({error:`Request body exceeds limit of ${H} bytes`,code:"payload_too_large"});return}if(ws(i)){if(t(i)){o.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:Ur(n).error,data:{code:"internal_error"}},id:null});return}o.status(500).json(Ur(n));return}s(n)}}a(Vf,"mcpJsonErrorHandler");function Yf(e,t){let n=e;n[Za]||(n[Za]=!0,e.use(Vf(t)))}a(Yf,"attachOnce");function Qa(e){for(let t of Xa(e))Yf(t,e.settings.httpAdminRoot)}a(Qa,"attachMcpJsonErrorHandlers");qn();var _t=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"}},Pt=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"}},Kn=class extends Error{static{a(this,"AccessLinkValidationError")}code="access_link_invalid";statusCode=400;constructor(t){super(t),this.name="AccessLinkValidationError"}},at=class extends Error{static{a(this,"AccessLinkTtlError")}code="invalid_ttl";statusCode=400;hint=At;constructor(t){super(t),this.name="AccessLinkTtlError"}};var Et=require("crypto");qn();function bs(e){return e==="get_link"||e==="rest"||e==="mcp"}a(bs,"isAccessLinkKind");function vs(e){return e??"get_link"}a(vs,"resolveAccessLinkKind");function ed(e){return(0,Et.createHash)("sha256").update(e,"utf8").digest("hex")}a(ed,"hashToken");function Xf(e,t){let n=Buffer.from(e,"utf8"),r=Buffer.from(t,"utf8");return n.length!==r.length?!1:(0,Et.timingSafeEqual)(n,r)}a(Xf,"hashesEqual");function Jn(e){let t={id:e.id,kind:vs(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(Jn,"toPublic");var Ct=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,Et.randomBytes)(12).toString("hex")),this.randomToken=t.randomToken??(()=>`aia_${(0,Et.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(Jn)}create(t={}){let n=this.now();if([...this.links.values()].filter(p=>this.isActive(p,n)).length>=this.maxActiveLinks)throw new _t(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 at("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=vs(t.kind),d=ss(i);if(o>d)throw new at(is(i,d));let l=this.randomId(),c=this.randomToken(),u={id:l,tokenHash:ed(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),{...Jn(u),token:c}}revoke(t){let n=this.links.get(t);if(!n)throw new Pt(t);return n.revokedAtMs??=this.now(),Jn(n)}getById(t){if(typeof t!="string"||t.length===0)return null;let n=this.links.get(t);return n?Jn(n):null}validateResult(t){if(typeof t!="string"||t.length===0)return{status:"invalid"};let n=ed(t),r=this.now();for(let o of this.links.values())if(Xf(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:Jn(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)}};qn();ce();ue();function ks(e,t){return t}a(ks,"storeForRequest");function nd(e){let t=typeof e.user?.username=="string"?e.user.username:void 0;return t?{kind:"admin",username:t}:{kind:"admin"}}a(nd,"adminActor");function uo(e){let t=ve(e.settings);return new Ct({defaultTtlSeconds:t.defaultLinkTtlSeconds,maxActiveLinks:t.maxActiveLinks})}a(uo,"createConfiguredLinkStore");function rd(e){e.setHeader("Cache-Control","no-store"),e.setHeader("Pragma","no-cache")}a(rd,"noStore");function od(e,t){return(n,r)=>{let s=ks(e,t).list();rd(r),r.json({links:s})}}a(od,"listLinksHandler");function sd(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:At});return}i.ttlSeconds=s.ttlSeconds}if(typeof s.label=="string"&&(i.label=s.label),s.kind!==void 0){if(!bs(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=ks(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",nd(r),{linkId:d.id,label:d.label,expiresAt:d.expiresAt,createdAt:d.createdAt,kind:l,...p!==void 0?{ttlSeconds:p}:{}})}if(rd(o),l==="get_link"){let c=ei(r,e.settings.httpAdminRoot,d.token);o.status(201).json({...d,url:c});return}o.status(201).json({...d})}catch(d){if(d instanceof _t){o.status(d.statusCode).json({error:d.message,code:d.code});return}if(d instanceof at){o.status(d.statusCode).json({error:d.message,code:d.code,hint:d.hint});return}if(d instanceof Kn){o.status(d.statusCode).json({error:d.message,code:d.code});return}D(o,d);return}}}a(sd,"createLinkHandler");function id(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=ks(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",nd(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 Pt){o.status(i.statusCode).json({error:i.message,code:i.code});return}D(o,i);return}}}a(id,"revokeLinkHandler");function Zf(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(Zf,"ttlSecondsFromLink");function Qf(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=Zf(l);c!==void 0&&(n.ttlSeconds=c)}return{...e,details:n}}a(Qf,"enrichAuditLogEntry");function ad(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=>Qf(d,t));r.json({logs:i})}}a(ad,"listLogsHandler");var Ss=require("@modelcontextprotocol/sdk/server/mcp.js"),hd=require("@modelcontextprotocol/sdk/server/streamableHttp.js"),R=require("zod");Ze();G();pt();J();De();wt();tt();Re();var dd=require("async_hooks");nt();var em="ai.redacted",ld=new dd.AsyncLocalStorage;function cd(e,t,n,r){if(!(r<=0))try{e.append(em,t,{redactions:r,operation:n})}catch{}}a(cd,"appendRedactionAudit");function ud(e){let t=ld.getStore();if(!t)return;let n=Fr(e);n>0&&t.record(n)}a(ud,"noteResponseRedactions");function pd(e,t,n,r){return ld.run({record:a(o=>cd(e,t,n,o),"record")},r)}a(pd,"runWithRedactionAudit");function fd(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(fd,"mcpOperationName");function As(e){let t=e.accessLink;if(t)return{kind:"access-link",linkId:t.id,...t.label!==void 0?{label:t.label}:{}}}a(As,"accessLinkAuditActor");function md(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=As(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"}`;cd(e,i,c,Fr(d))}return o(s)}),r()}}a(md,"redactionAuditMiddleware");nt();function gd(){return"0.2.0-beta.5"}a(gd,"pluginVersion");async function yd(e){try{return(await ye(e)).rev}catch{return}}a(yd,"tryLiveRev");function Q(e){let t=JSON.stringify(e);return ud(t),{content:[{type:"text",text:t}]}}a(Q,"jsonResult");function xs(e){return e===!0?new Set(["payload","baseFlow"]):new Set}a(xs,"includeSetFromPayloadFlag");function ee(e,t=500){let n=Bn(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(ee,"jsonError");var tm=new Set(["list_proposals","list_my_proposals"]),nm=new Set(["status","limit","offset","mine","flowId"]);function rm(e){return!!e&&typeof e=="object"&&typeof e.safeParseAsync=="function"}a(rm,"isZodLikeSchema");function om(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(tm.has(e)&&nm.has(s))return Xt(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 y(d,"invalid_arguments",{details:{parameter:s,errors:i,hint:`Fix '${s}' to match the ${e} input schema (see tools/list).`}})}a(om,"mcpArgumentError");function wd(e){let t=e,n=t.validateToolInput;typeof n=="function"&&(t.validateToolInput=async(r,o,s)=>{let i=r.inputSchema;if(!rm(i))return n.call(e,r,o,s);let d=await i.safeParseAsync(o??{});if(d.success)return d.data;let l=Bn(om(s,o,d.error.issues),"mcp"),c=l?{...l.body,statusCode:l.statusCode}:{};throw new Error(JSON.stringify(c))})}a(wd,"installMcpArgumentErrorMapping");function bd(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 Q(pn(t,r))}catch(o){return ee(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 Q(await ye(n.flowAdapter))}catch(o){return ee(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:R.z.string().min(1).describe("Flow / tab id"),summary:R.z.boolean().optional().describe("When true, return node list with sizes only (no field contents)")}},async({id:o,summary:s})=>{try{return Q(s?await Gr(n.flowAdapter,o):await _e(n.flowAdapter,o))}catch(i){return ee(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:R.z.string().min(1).describe("Node id"),flow:R.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,flow:s})=>{try{return Q(await Kr(n.flowAdapter,o,s))}catch(i){return ee(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:R.z.string().min(1).describe("Node id"),field:R.z.string().min(1).describe("Field name (e.g. template, func)"),lines:R.z.string().optional().describe("Optional 1-based line range A or A-B"),flow:R.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,field:s,lines:i,flow:d})=>{try{return Q(await Jr(n.flowAdapter,o,s,{...d?{flowId:d}:{},...i?{lines:i}:{}}))}catch(l){return ee(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:R.z.string().describe("Substring to find (case-sensitive; non-empty required)"),flow:R.z.string().min(1).optional().describe("Optional tab id")}},async({q:o,flow:s})=>{try{return Q(await Vr(n.flowAdapter,o,s?{flowId:s}:{}))}catch(i){return ee(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:R.z.string().optional().describe("Optional case-insensitive filter")}},async({q:o})=>{try{return Q(await xr(n.nodeTypesAdapter,o?{q:o}:{}))}catch(s){return ee(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:R.z.string().min(1).describe("Node type id (e.g. inject, template) \u2014 case-sensitive"),includeHelp:R.z.boolean().optional().describe("When false, omit help sections (default true)")}},async({type:o,includeHelp:s})=>{try{return Q(await yt(n.nodeTypesAdapter,o,{includeHelp:s!==!1}))}catch(i){return ee(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:R.z.string().describe("Search string (id / description / keywords; non-empty required)")}},async({q:o})=>{try{return Q(await Tr(n.nodeTypesAdapter,o,{...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{}}))}catch(s){return ee(s,502)}})}a(bd,"registerReadTools");function vd(e,t,n,r,o){let s=q(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 ${H} bytes (413 if exceeded). expectedRev is checked against the live rev at submit time (409 if stale). 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:R.z.literal("replace_flow").describe("Must be 'replace_flow'"),expectedRev:R.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:R.z.string().min(1).optional().describe("Alias for expectedRev (snake_case); prefer expectedRev"),title:R.z.string().max(80).optional().describe("Optional short human title (\u226480 chars). Auto-generated from changes when omitted."),include_payload:R.z.boolean().optional().describe("When true, include payload and baseFlow in the response (default lean)"),nodes:R.z.array(R.z.record(R.z.string(),R.z.unknown())).optional().describe("Optional node ops (set/add/remove/replace_text). Same as payload.nodes when flow is omitted."),payload:R.z.object({flowId:R.z.string().min(1).describe("Tab id to replace"),flow:R.z.object({id:R.z.string().optional(),type:R.z.string().optional().describe("Usually 'tab' (included by get_flow)"),label:R.z.string().optional(),nodes:R.z.array(R.z.record(R.z.string(),R.z.unknown())).optional().describe("Child nodes for the tab (from get_flow.nodes)"),configs:R.z.array(R.z.record(R.z.string(),R.z.unknown())).optional()}).passthrough().optional().describe("Full tab from get_flow; omit when using nodes ops"),nodes:R.z.array(R.z.record(R.z.string(),R.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 rt(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={...he(d,{...s,include:xs(i.include_payload)}),mine:!0},c=V(n.proposalStore,d.expectedRev,{flowId:d.payload.flowId}),u=$e(Oe(n.proposalStore,n.proposalStore.get(d.id),o.id));return Q({...l,...u,...Z(c)})}catch(d){return ee(d)}}),im(e,n,o,s),am(e,n,o,s)}a(vd,"registerProposalTools");function sm(e,t,n,r){let o={mine:R.z.boolean().optional().describe("true: only proposals created by this access link; false: only other links\u2019 proposals"),status:R.z.union([R.z.string(),R.z.array(R.z.string())]).optional().describe("Filter by proposal status: one value, an array, or a comma-separated string (pending, approved, rejected, failed, withdrawn, expired, superseded). An empty array or empty string means no status filter."),flowId:R.z.string().optional().describe("Filter by flow tab id (payload.flowId)"),limit:R.z.number().int().min(1).max(Yt).optional().describe(`Page size (1\u2013${Yt}). Omit to return all matches.`),offset:R.z.number().int().min(0).optional().describe("Skip this many filtered results (default 0)"),include_payload:R.z.boolean().optional().describe("When true, include payload and baseFlow on each proposal")},s=a(async i=>{try{let d=xs(i.include_payload),l=await yd(t.flowAdapter),c=l?V(t.proposalStore,l):0,u={};typeof i.mine=="boolean"&&(u.mine=i.mine);let p=os(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=Zt(t.proposalStore,n,r,u);return Q({proposals:f.proposals.map(m=>ge(t.proposalStore,n,m,{...r,include:d},{includeConflicts:!1})),...kt({...f,returned:f.proposals.length}),...Z(c)})}catch(d){return ee(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(sm,"registerListProposalsTools");function im(e,t,n,r){sm(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 pending proposals, conflictingProposals when other pending work touches the same flow. Lean by default (no payload/baseFlow); pass include_payload=true for full bodies. Cite displayName + reviewUrl when talking to the user.",inputSchema:{id:R.z.string().min(1).describe("Proposal id (prop-\u2026)"),include_payload:R.z.boolean().optional().describe("When true, include payload and baseFlow")}},async({id:o,include_payload:s})=>{try{let i=await yd(t.flowAdapter),d=ze(t.proposalStore,n,o,r);return Q(ge(t.proposalStore,n,d,{...r,include:xs(s)},{...i?{liveRev:i}:{}}))}catch(i){return ee(i)}})}a(im,"registerProposalQueryTools");function am(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). Pending + stale also includes vsCurrent.",inputSchema:{id:R.z.string().min(1).describe("Proposal id (prop-\u2026)"),format:R.z.enum(["summary","full"]).optional().describe("Diff payload format (default summary)")}},async({id:o,format:s})=>{try{let i=ze(t.proposalStore,n,o),d=sn(s??"summary");if(!d.ok)return ee(new y("Invalid diff format","invalid_diff_format",{details:{hint:"Valid formats: summary, full (default summary)."}}));let l=an(await un(i,t.flowAdapter),d.format);return Q(l)}catch(i){return ee(i)}}),e.registerTool("withdraw_proposal",{description:"Withdraw one of YOUR pending proposals (status becomes withdrawn, not rejected). Optional reason. Only the creating access link can withdraw; other tokens get 403. Cite displayName + reviewUrl when telling the user.",inputSchema:{id:R.z.string().min(1).describe("Proposal id (prop-\u2026) owned by this access link"),reason:R.z.string().optional().describe("Optional withdraw reason")}},({id:o,reason:s})=>{try{let i=Qt(t.proposalStore,t.auditLog,n,o,s,r);return Q(ge(t.proposalStore,n,i,r))}catch(i){return ee(i)}})}a(am,"registerProposalActionTools");function dm(e,t,n){let r=new Ss.McpServer({name:"node-red-ai-access",version:gd()});wd(r);let o=n.accessLink;if(!o)throw new Error("Access link required");return bd(r,e,t,n),vd(r,e,t,n,o),r}a(dm,"createAiMcpServer");function lm(e){let t=As(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:vr(n,r),label:"oauth"}}a(lm,"mcpAuditActor");function Ts(e,t){return async(n,r,o)=>{try{if(!n.accessLink&&!n.auth){r.setHeader("WWW-Authenticate",X),r.status(401).json({error:me,code:Ie});return}let s={...t,flowAdapter:bt(t.flowAdapter)},i=n.accessLink?dm(e,s,n):cm(e,s,n),d=new hd.StreamableHTTPServerTransport({enableJsonResponse:!0});await i.connect(d),await pd(t.auditLog,lm(n),fd(n.body),()=>d.handleRequest(n,r,n.body)),r.on("close",()=>{d.close(),i.close()})}catch(s){if(!r.headersSent){D(r,s);return}o(s instanceof Error?s:new Error(String(s)))}}}a(Ts,"mcpRpcPostHandler");function cm(e,t,n){let r=new Ss.McpServer({name:"node-red-ai-access",version:gd()});if(wd(r),bd(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:vr(i,s),kind:"mcp",createdAt:new Date().toISOString(),expiresAt:new Date((n.auth?.expiresAt??0)*1e3).toISOString(),createdBy:s,oauthClientId:i,oauthUser:s};vd(r,e,t,n,l)}return r}a(cm,"createOAuthMcpServer");function Vn(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(Vn,"mcpRpcMethodNotAllowed");var Yn=require("crypto"),mn=require("fs"),Ps=require("path"),B=require("@modelcontextprotocol/sdk/server/auth/errors.js"),Td=require("@modelcontextprotocol/sdk/server/auth/handlers/authorize.js"),Es=require("@modelcontextprotocol/sdk/server/auth/handlers/metadata.js"),Rd=require("@modelcontextprotocol/sdk/server/auth/handlers/register.js"),_d=require("@modelcontextprotocol/sdk/server/auth/handlers/revoke.js"),Pd=require("@modelcontextprotocol/sdk/server/auth/handlers/token.js"),Ed=require("@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js"),Cd=require("@modelcontextprotocol/sdk/server/auth/router.js"),Ld=yn(require("express"),1);G();Le();function He(e){return e.replace(/[&<>"']/g,t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[t]??t)}a(He,"esc");var kd={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>'},um={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."]},pm='<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>',fm=`
954
957
  :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}
955
958
  *{box-sizing:border-box}
956
959
  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}
@@ -986,7 +989,7 @@ form.login input{font:inherit;padding:9px 11px;border-radius:10px;border:1px sol
986
989
  a{color:var(--focus)}
987
990
  [hidden]{display:none!important}
988
991
  @media (max-width:420px){.actions{flex-direction:column-reverse}.secondary{flex:1}}
989
- `;function Aa(e){let t=e.scopes.map(n=>{let[r,o]=Ep[n]??[n,""];return`<li data-scope="${$e(n)}">${ka[n]??ka.read}<div><b>${$e(r)}</b><span>${$e(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>${Np}</style></head><body><main class="card" data-request="${$e(e.requestId)}" data-token-key="${$e(e.tokenKey)}" data-admin-root="${$e(e.adminRoot)}"><div class="head">${Ip}<div class="brand"><b>Node-RED AI Access</b><span>Authorization request</span></div></div><div class="body"><h1><span class="app">${$e(e.redirectHost)}</span> wants to access your Node-RED</h1><p class="sub">Access tokens will be sent to <span class="host">${$e(e.redirectHost)}</span>. App name (provided by the client, unverified): <b>${$e(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="${$e(e.nonce)}">${e.script}</script></body></html>`}a(Aa,"renderConsentPage");var xa=`(function () {
992
+ `;function Ad(e){let t=e.scopes.map(n=>{let[r,o]=um[n]??[n,""];return`<li data-scope="${He(n)}">${kd[n]??kd.read}<div><b>${He(r)}</b><span>${He(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>${fm}</style></head><body><main class="card" data-request="${He(e.requestId)}" data-token-key="${He(e.tokenKey)}" data-admin-root="${He(e.adminRoot)}"><div class="head">${pm}<div class="brand"><b>Node-RED AI Access</b><span>Authorization request</span></div></div><div class="body"><h1><span class="app">${He(e.redirectHost)}</span> wants to access your Node-RED</h1><p class="sub">Access tokens will be sent to <span class="host">${He(e.redirectHost)}</span>. App name (provided by the client, unverified): <b>${He(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="${He(e.nonce)}">${e.script}</script></body></html>`}a(Ad,"renderConsentPage");var Sd=`(function () {
990
993
  var main = document.querySelector('main');
991
994
  var id = main.getAttribute('data-request');
992
995
  var key = main.getAttribute('data-token-key');
@@ -1102,7 +1105,7 @@ a{color:var(--focus)}
1102
1105
  deny.addEventListener('click', function () { decide('deny'); });
1103
1106
  retry.addEventListener('click', load);
1104
1107
  load();
1105
- })();`;ye();var nt=a(e=>(0,$n.createHash)("sha256").update(e).digest("hex"),"hash"),St=a(()=>(0,$n.randomBytes)(32).toString("base64url"),"secret"),Fp=36e5,Op=25,$p=5,Mp=500,Sa=a(()=>({clients:[],codes:[],tokens:[]}),"emptyDb"),Br=class{constructor(t,n){this.maxClients=n;this.path=(0,ts.join)(t.root,"oauth.json");try{let r=(0,sn.existsSync)(this.path)?JSON.parse((0,sn.readFileSync)(this.path,"utf8")):Sa();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=Sa()}(0,sn.mkdirSync)(t.root,{recursive:!0,mode:448}),this.prune()}static{a(this,"OAuthStore")}db;path;save(){Rt((0,ts.join)(this.path,".."),this.path,Be(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-Fp).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 B.InvalidClientMetadataError("Maximum active OAuth clients reached");if(this.db.clients.length-this.approvedCount()>=Op)throw new B.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 B.InvalidClientMetadataError("Redirect URIs must use HTTPS or loopback HTTP");if(t.token_endpoint_auth_method&&t.token_endpoint_auth_method!=="none")throw new B.InvalidClientMetadataError("Only public clients are supported");let n={...t,client_secret:void 0,client_secret_expires_at:void 0,client_id:St(),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 B.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 os(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(os,"describeOAuthRouteCollision");var ss=a((e,t)=>e.toLowerCase().startsWith(t.toLowerCase()),"startsWithCi");function Zo(e){return e===!1?void 0:`/${typeof e=="string"?e:"/"}`.replace(/\/+/g,"/").replace(/\/$/,"")}a(Zo,"normalizeRoot");function es(e,t){if(e!==void 0){if(!e)return t;if(ss(t,`${e}/`))return t.slice(e.length)}}a(es,"relativePrefix");function Dp(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(Dp,"makeLiveCheck");function jp(e,t){if(!e||!t.length)return[];let n=[];try{e(s=>n.push(s))}catch{return[]}let r=Dp(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=>ss(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(jp,"findHttpInCollisions");var Hp=new Set(["bound dispatch","handle"]),Up=["authorize","token","register","revoke"],Bp=["authorization-server","protected-resource"];function qp(e){let t=e.length;for(;t>0&&e.charCodeAt(t-1)===47;)t--;return e.slice(0,t)}a(qp,"trimTrailingSlashes");function Wp(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(Wp,"dedupe");function Ia(e){let t=Zo(e.httpAdminRoot),n=e.httpNode?Zo(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=es(t,p),m=es(n,p),h=m?jp(e.eachNode,[m]):[],g=new Set(h.map(E=>E.path.toLowerCase())),x=m?rs(e.httpNode,[m]).filter(E=>!(E.source==="route"&&Hp.has(E.layer??"")&&g.has(E.path.toLowerCase()))):[],S=f?rs(e.httpAdmin,[f]):[];return Wp([...S,...x,...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=Zo(m.root),g=qp(m.path);for(let x of p){let S=es(h,x);!S||!l(`${g}${S}`)||f.push({path:x,source:"httpStatic",file:`${g}${S}`})}}},"checkStatic");return u(Up.map(p=>`${r}${p}`),i),u(Bp.map(p=>`${o}${p}`),d),{authorization:i,metadata:d}}a(Ia,"scanOAuthRouteCollisions");function zp(e){let t=a(n=>{try{return e?.[n]}catch{return}},"read");return t("_router")?.stack??t("router")?.stack??[]}a(zp,"routerStack");function Gp(e){let t=e.route?.path;return t?(Array.isArray(t)?t:[t]).map(String):[]}a(Gp,"layerPaths");function Jp(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(Jp,"middlewareHits");function rs(e,t=["/.well-known/oauth-","/mcp/oauth/"]){let n=[];for(let r of zp(e)){if(r.handle?.oauthOwned)continue;let o=Gp(r);for(let s of o)t.some(i=>ss(s,i))&&n.push({path:s,source:"route",layer:r.name??"route"});!o.length&&Jp(r,t)&&n.push({path:t.join(", "),source:"middleware",layer:r.name??"route"}),r.handle?.stack&&n.push(...rs({router:{stack:r.handle.stack}},t))}return n}a(rs,"detectOAuthRouteCollisionDetails");var qr=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 B.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(nt(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&&rt(s.href)!==i.resource)throw new B.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===nt(n)&&l.type==="refresh"&&l.clientId===t.client_id);if(!s||s.expires<=Date.now())throw new B.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 B.InvalidGrantError("Refresh token reuse detected");let i=this.refreshMax()*1e3,d=s.familyStart??Date.now();if(i>0&&d+i<=Date.now())throw new B.InvalidGrantError("Refresh token family reached its maximum lifetime");if(o&&rt(o.href)!==s.resource)throw new B.InvalidGrantError("Resource mismatch");if(r?.some(l=>!s.scopes.includes(l)))throw new B.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===nt(t)&&o.type==="access"&&o.expires>Date.now());if(!n||n.resource!==rt(this.resource()))throw new B.InvalidTokenError("Invalid access token");let r=this.clientsStore.data.clients.find(o=>o.client_id===n.clientId);if(!r)throw new B.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===nt(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=St();return this.clientsStore.data.codes.push({hash:nt(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=rt(this.resource());if(t&&rt(t.href)!==n)throw new B.InvalidGrantError("Resource mismatch");return n}findCode(t,n){let r=this.clientsStore.data.codes.find(o=>o.hash===nt(n)&&o.clientId===t.client_id);if(!r||r.expires<=Date.now())throw new B.InvalidGrantError("Invalid or expired authorization code");return r}issue(t,n,r,o,s=St(),i=Date.now()){let d=St(),l=St(),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:nt(d),type:"access",clientId:t,family:s,scopes:n,resource:r,expires:c+this.accessTtl()*1e3,user:o,rotated:!1},{hash:nt(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 rt(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(rt,"canonicalResource");var on=["read","propose"];function Kp(e){let t=(e??"/").split("/").filter(Boolean).join("/");return t?`/${t}/`:"/"}a(Kp,"consentAdminRoot");var Vp=new Set(["localhost","127.0.0.1","[::1]"]);function xt(e){return typeof e=="string"?e:""}a(xt,"value");function Na(e,t,n,r){let o=a(A=>`${n().replace(/\/$/,"")}${A}`,"abs"),s=a((A,v,w,b)=>{let k=v.url;v.url="/",A(v,w,L=>{v.url=k,b(L)})},"atRoot"),i=new Map,d=a(()=>{let A=Date.now();for(let[v,w]of i)w.expires<=A&&i.delete(v)},"sweep"),l=a(A=>{d();let v=[...i].filter(([,w])=>w.clientId===A);for(;v.length>=$p;)i.delete(v.shift()[0]);return i.size<Mp},"reservePending"),c=a((A,v)=>{A.setHeader("Cache-Control","no-store"),A.setHeader("X-Frame-Options","DENY"),A.setHeader("Referrer-Policy","no-referrer"),A.setHeader("X-Content-Type-Options","nosniff"),A.setHeader("Content-Security-Policy",`default-src 'none'; ${v?`script-src 'nonce-${v}'; `:""}style-src 'unsafe-inline'; connect-src 'self'; form-action 'none'; base-uri 'none'; frame-ancestors 'none'`)},"securityHeaders"),u=Yn(e.settings.httpAdminRoot),p=a(A=>{if(!W(e.settings))return[...on];let v=We(A);return v==="*"?[...on]:v==="read"?["read"]:[]},"grantableFor"),f=a(A=>{let v=A.get("sec-fetch-site");if(v&&v!=="same-origin")return!1;let w=A.get("origin");if(!w)return!0;let b=new Set;try{b.add(new URL(n()).origin)}catch{}let k=A.get("host");return k&&(b.add(`http://${k}`),b.add(`https://${k}`)),b.has(w)},"sameOrigin"),m=a((A,v)=>{d();let w=A.query,b=t.clientsStore.getClient(xt(w.client_id));if(!b){v.status(400).send("Unknown OAuth client");return}let k=xt(w.redirect_uri)||(b.redirect_uris.length===1?b.redirect_uris[0]:"");if(!b.redirect_uris.some(Xr=>(0,Ta.redirectUriMatches)(k,Xr))){v.status(400).send("Unregistered redirect URI");return}if(w.response_type!=="code"||w.code_challenge_method!=="S256"||!/^[A-Za-z0-9_-]{43,128}$/.test(xt(w.code_challenge))){v.status(400).send("Authorization code with PKCE S256 is required");return}let L=[...new Set((xt(w.scope)||"read").split(" ").filter(Boolean))];if(L.some(Xr=>!on.includes(Xr))){v.status(400).send("Unsupported scope");return}let H=xt(w.resource)||r(),M;try{M=rt(H)}catch{v.status(400).send("Invalid resource");return}if(M!==rt(r())){v.status(400).send("Resource mismatch");return}if(!l(b.client_id)){v.status(429).send("Too many pending authorization requests; try again later");return}let Re=St();i.set(Re,{clientId:b.client_id,redirect:k,challenge:xt(w.code_challenge),scopes:L,resource:H,state:xt(w.state),csrf:St(),expires:Date.now()+10*6e4});let Ue=new URL(k).host,Pt=(0,$n.randomBytes)(16).toString("base64");c(v,Pt),v.status(200).type("html").send(Aa({requestId:Re,tokenKey:u,adminRoot:Kp(e.settings.httpAdminRoot),nonce:Pt,redirectHost:Ue,loopbackRedirect:Vp.has(new URL(k).hostname),clientName:b.client_name||"Unnamed client",scopes:L,script:xa}))},"authorizePage"),h=a((A,v,w)=>{c(v),Ea.default.json({limit:"4kb"})(A,v,()=>we(e,"api")(A,v,()=>{try{d();let b=/^\/mcp\/oauth\/consent\/([A-Za-z0-9_-]+)\/?$/.exec(A.path)?.[1]??"",k=i.get(b),L=k&&t.clientsStore.getClient(k.clientId);if(!k||!L){v.status(404).json({error:"Consent request expired or unknown"});return}let H=W(e.settings)&&A.user?.username||"owner",M=k.scopes.filter(Pt=>p(A).includes(Pt));if(!M.length){v.status(403).json({error:"Your Node-RED permissions do not allow granting the requested scopes",grantable:M});return}if(A.method==="GET"){k.approver=H,v.json({client_name:L.client_name??null,redirect_host:new URL(k.redirect).host,requested:k.scopes,grantable:M,user:H,csrf:k.csrf});return}if(A.method!=="POST"){v.status(405).end();return}let Re=A.body??{};if(!f(A)||Re.csrf!==k.csrf||k.approver!==H){v.status(403).json({error:"Consent request verification failed"});return}i.delete(b);let Ue=new URL(k.redirect);if(Re.decision!=="allow")Ue.searchParams.set("error","access_denied");else{try{t.clientsStore.approve(L.client_id)}catch{v.status(409).json({error:"Maximum active OAuth clients reached"});return}let Pt=t.createCode(L,{redirectUri:k.redirect,codeChallenge:k.challenge,scopes:M,resource:new URL(k.resource)},H);Ue.searchParams.set("code",Pt)}k.state&&Ue.searchParams.set("state",k.state),v.json({redirect:Ue.href})}catch(b){w(b)}}))},"consentApi"),g=(0,Pa.clientRegistrationHandler)({clientsStore:t.clientsStore,rateLimit:{windowMs:36e5,max:10,standardHeaders:!0,legacyHeaders:!1}}),x=(0,_a.tokenHandler)({provider:t}),S=(0,Ra.revocationHandler)({provider:t}),E,I="";return{server:a((A,v,w)=>{let b=A.path,k=b.replace(/\/+$/,"").toLowerCase();if(k==="/mcp/oauth/authorize"){A.method==="GET"?m(A,v,w):v.status(405).set("Allow","GET").end();return}if(k.startsWith("/mcp/oauth/consent/")){h(A,v,w);return}if(!["/mcp/oauth/token","/mcp/oauth/register","/mcp/oauth/revoke"].includes(k)){w();return}let L=n();if(L.startsWith("https://")||/^http:\/\/(localhost|127\.0\.0\.1)(:|\/|$)/.test(L)){if(I!==L)try{E=(0,Ca.mcpAuthRouter)({provider:t,issuerUrl:new URL(L),baseUrl:new URL(`${L.replace(/\/$/,"")}/mcp/oauth/`),resourceServerUrl:new URL(r()),scopesSupported:on,clientRegistrationOptions:{rateLimit:{windowMs:36e5,max:10}}}),I=L}catch{E=void 0}if(E){let H=A.url;A.url=A.url.slice(10)||"/",E(A,v,M=>{A.url=H,w(M)});return}}if(b==="/mcp/oauth/token"){s(x,A,v,w);return}if(b==="/mcp/oauth/register"){s(g,A,v,w);return}if(b==="/mcp/oauth/revoke"){s(S,A,v,w);return}w()},"server"),metadata:a((A,v,w)=>{let b=A.path;if(b==="/.well-known/oauth-authorization-server"){s((0,ns.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:on}),A,v,w);return}if(b==="/.well-known/oauth-protected-resource/mcp"||b==="/.well-known/oauth-protected-resource"){s((0,ns.metadataHandler)({resource:rt(r()),authorization_servers:[n()],scopes_supported:on}),A,v,w);return}w()},"metadata"),auth:a((A,v,w)=>{try{let b=v.set.bind(v);v.set=((k,L)=>(k==="WWW-Authenticate"&&typeof L=="string"&&(L=L.replace('scope="read"','scope="read propose"')),typeof k=="string"?b(k,L??""):b(k))),(0,La.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())})(A,v,k=>{v.set=b,w(k)})}catch(b){w(b)}},"auth")}}a(Na,"makeOAuthHandlers");var Mn=require("fs"),an=require("path"),Fa=require("url"),Oa=require("yaml"),Zp={};function Xp(){return typeof __dirname<"u"?__dirname:(0,an.dirname)((0,Fa.fileURLToPath)(Zp.url))}a(Xp,"moduleDir");function $a(){let e=Xp(),t=[(0,an.join)(e,"..","docs","api","openapi.yaml"),(0,an.join)(e,"..","..","docs","api","openapi.yaml"),(0,an.join)(process.cwd(),"docs","api","openapi.yaml")];for(let n of t)if((0,Mn.existsSync)(n))return n;return null}a($a,"resolveOpenApiYamlPath");function Yp(){let e=$a();if(!e)throw new Error("OpenAPI specification file was not found");return(0,Mn.readFileSync)(e,"utf8")}a(Yp,"loadOpenApiYaml");function Ma(){return(e,t)=>{try{let n=Yp();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(Ma,"openApiYamlHandler");var is;function Qp(){let e=$a();if(!e)throw new Error("OpenAPI specification file was not found");return is?.path!==e&&(is={path:e,json:JSON.stringify((0,Oa.parse)((0,Mn.readFileSync)(e,"utf8")))}),is.json}a(Qp,"loadOpenApiJson");function Da(){return(e,t)=>{try{t.status(200).type("application/json").send(Qp())}catch(n){let r=n instanceof Error?n.message:String(n);t.status(500).json({error:r})}}}a(Da,"openApiJsonHandler");function ef(){return`
1108
+ })();`;ue();var dt=a(e=>(0,Yn.createHash)("sha256").update(e).digest("hex"),"hash"),It=a(()=>(0,Yn.randomBytes)(32).toString("base64url"),"secret"),mm=36e5,hm=25,gm=5,ym=500,xd=a(()=>({clients:[],codes:[],tokens:[]}),"emptyDb"),po=class{constructor(t,n){this.maxClients=n;this.path=(0,Ps.join)(t.root,"oauth.json");try{let r=(0,mn.existsSync)(this.path)?JSON.parse((0,mn.readFileSync)(this.path,"utf8")):xd();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=xd()}(0,mn.mkdirSync)(t.root,{recursive:!0,mode:448}),this.prune()}static{a(this,"OAuthStore")}db;path;save(){Ot((0,Ps.join)(this.path,".."),this.path,Je(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-mm).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 B.InvalidClientMetadataError("Maximum active OAuth clients reached");if(this.db.clients.length-this.approvedCount()>=hm)throw new B.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 B.InvalidClientMetadataError("Redirect URIs must use HTTPS or loopback HTTP");if(t.token_endpoint_auth_method&&t.token_endpoint_auth_method!=="none")throw new B.InvalidClientMetadataError("Only public clients are supported");let n={...t,client_secret:void 0,client_secret_expires_at:void 0,client_id:It(),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 B.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 Ls(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(Ls,"describeOAuthRouteCollision");var Is=a((e,t)=>e.toLowerCase().startsWith(t.toLowerCase()),"startsWithCi");function Rs(e){return e===!1?void 0:`/${typeof e=="string"?e:"/"}`.replace(/\/+/g,"/").replace(/\/$/,"")}a(Rs,"normalizeRoot");function _s(e,t){if(e!==void 0){if(!e)return t;if(Is(t,`${e}/`))return t.slice(e.length)}}a(_s,"relativePrefix");function wm(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(wm,"makeLiveCheck");function bm(e,t){if(!e||!t.length)return[];let n=[];try{e(s=>n.push(s))}catch{return[]}let r=wm(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=>Is(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(bm,"findHttpInCollisions");var vm=new Set(["bound dispatch","handle"]),km=["authorize","token","register","revoke"],Am=["authorization-server","protected-resource"];function Sm(e){let t=e.length;for(;t>0&&e.charCodeAt(t-1)===47;)t--;return e.slice(0,t)}a(Sm,"trimTrailingSlashes");function xm(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(xm,"dedupe");function Id(e){let t=Rs(e.httpAdminRoot),n=e.httpNode?Rs(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=_s(t,p),m=_s(n,p),h=m?bm(e.eachNode,[m]):[],g=new Set(h.map(C=>C.path.toLowerCase())),k=m?Cs(e.httpNode,[m]).filter(C=>!(C.source==="route"&&vm.has(C.layer??"")&&g.has(C.path.toLowerCase()))):[],T=f?Cs(e.httpAdmin,[f]):[];return xm([...T,...k,...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=Rs(m.root),g=Sm(m.path);for(let k of p){let T=_s(h,k);!T||!l(`${g}${T}`)||f.push({path:k,source:"httpStatic",file:`${g}${T}`})}}},"checkStatic");return u(km.map(p=>`${r}${p}`),i),u(Am.map(p=>`${o}${p}`),d),{authorization:i,metadata:d}}a(Id,"scanOAuthRouteCollisions");function Tm(e){let t=a(n=>{try{return e?.[n]}catch{return}},"read");return t("_router")?.stack??t("router")?.stack??[]}a(Tm,"routerStack");function Rm(e){let t=e.route?.path;return t?(Array.isArray(t)?t:[t]).map(String):[]}a(Rm,"layerPaths");function _m(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(_m,"middlewareHits");function Cs(e,t=["/.well-known/oauth-","/mcp/oauth/"]){let n=[];for(let r of Tm(e)){if(r.handle?.oauthOwned)continue;let o=Rm(r);for(let s of o)t.some(i=>Is(s,i))&&n.push({path:s,source:"route",layer:r.name??"route"});!o.length&&_m(r,t)&&n.push({path:t.join(", "),source:"middleware",layer:r.name??"route"}),r.handle?.stack&&n.push(...Cs({router:{stack:r.handle.stack}},t))}return n}a(Cs,"detectOAuthRouteCollisionDetails");var fo=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 B.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(dt(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&&lt(s.href)!==i.resource)throw new B.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===dt(n)&&l.type==="refresh"&&l.clientId===t.client_id);if(!s||s.expires<=Date.now())throw new B.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 B.InvalidGrantError("Refresh token reuse detected");let i=this.refreshMax()*1e3,d=s.familyStart??Date.now();if(i>0&&d+i<=Date.now())throw new B.InvalidGrantError("Refresh token family reached its maximum lifetime");if(o&&lt(o.href)!==s.resource)throw new B.InvalidGrantError("Resource mismatch");if(r?.some(l=>!s.scopes.includes(l)))throw new B.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===dt(t)&&o.type==="access"&&o.expires>Date.now());if(!n||n.resource!==lt(this.resource()))throw new B.InvalidTokenError("Invalid access token");let r=this.clientsStore.data.clients.find(o=>o.client_id===n.clientId);if(!r)throw new B.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===dt(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=It();return this.clientsStore.data.codes.push({hash:dt(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=lt(this.resource());if(t&&lt(t.href)!==n)throw new B.InvalidGrantError("Resource mismatch");return n}findCode(t,n){let r=this.clientsStore.data.codes.find(o=>o.hash===dt(n)&&o.clientId===t.client_id);if(!r||r.expires<=Date.now())throw new B.InvalidGrantError("Invalid or expired authorization code");return r}issue(t,n,r,o,s=It(),i=Date.now()){let d=It(),l=It(),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:dt(d),type:"access",clientId:t,family:s,scopes:n,resource:r,expires:c+this.accessTtl()*1e3,user:o,rotated:!1},{hash:dt(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 lt(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(lt,"canonicalResource");var fn=["read","propose"];function Pm(e){let t=(e??"/").split("/").filter(Boolean).join("/");return t?`/${t}/`:"/"}a(Pm,"consentAdminRoot");var Em=new Set(["localhost","127.0.0.1","[::1]"]);function Lt(e){return typeof e=="string"?e:""}a(Lt,"value");function Nd(e,t,n,r){let o=a(S=>`${n().replace(/\/$/,"")}${S}`,"abs"),s=a((S,b,w,v)=>{let A=b.url;b.url="/",S(b,w,E=>{b.url=A,v(E)})},"atRoot"),i=new Map,d=a(()=>{let S=Date.now();for(let[b,w]of i)w.expires<=S&&i.delete(b)},"sweep"),l=a(S=>{d();let b=[...i].filter(([,w])=>w.clientId===S);for(;b.length>=gm;)i.delete(b.shift()[0]);return i.size<ym},"reservePending"),c=a((S,b)=>{S.setHeader("Cache-Control","no-store"),S.setHeader("X-Frame-Options","DENY"),S.setHeader("Referrer-Policy","no-referrer"),S.setHeader("X-Content-Type-Options","nosniff"),S.setHeader("Content-Security-Policy",`default-src 'none'; ${b?`script-src 'nonce-${b}'; `:""}style-src 'unsafe-inline'; connect-src 'self'; form-action 'none'; base-uri 'none'; frame-ancestors 'none'`)},"securityHeaders"),u=mr(e.settings.httpAdminRoot),p=a(S=>{if(!z(e.settings))return[...fn];let b=Ye(S);return b==="*"?[...fn]:b==="read"?["read"]:[]},"grantableFor"),f=a(S=>{let b=S.get("sec-fetch-site");if(b&&b!=="same-origin")return!1;let w=S.get("origin");if(!w)return!0;let v=new Set;try{v.add(new URL(n()).origin)}catch{}let A=S.get("host");return A&&(v.add(`http://${A}`),v.add(`https://${A}`)),v.has(w)},"sameOrigin"),m=a((S,b)=>{d();let w=S.query,v=t.clientsStore.getClient(Lt(w.client_id));if(!v){b.status(400).send("Unknown OAuth client");return}let A=Lt(w.redirect_uri)||(v.redirect_uris.length===1?v.redirect_uris[0]:"");if(!v.redirect_uris.some(vo=>(0,Td.redirectUriMatches)(A,vo))){b.status(400).send("Unregistered redirect URI");return}if(w.response_type!=="code"||w.code_challenge_method!=="S256"||!/^[A-Za-z0-9_-]{43,128}$/.test(Lt(w.code_challenge))){b.status(400).send("Authorization code with PKCE S256 is required");return}let E=[...new Set((Lt(w.scope)||"read").split(" ").filter(Boolean))];if(E.some(vo=>!fn.includes(vo))){b.status(400).send("Unsupported scope");return}let $=Lt(w.resource)||r(),F;try{F=lt($)}catch{b.status(400).send("Invalid resource");return}if(F!==lt(r())){b.status(400).send("Resource mismatch");return}if(!l(v.client_id)){b.status(429).send("Too many pending authorization requests; try again later");return}let le=It();i.set(le,{clientId:v.client_id,redirect:A,challenge:Lt(w.code_challenge),scopes:E,resource:$,state:Lt(w.state),csrf:It(),expires:Date.now()+10*6e4});let Ke=new URL(A).host,Ft=(0,Yn.randomBytes)(16).toString("base64");c(b,Ft),b.status(200).type("html").send(Ad({requestId:le,tokenKey:u,adminRoot:Pm(e.settings.httpAdminRoot),nonce:Ft,redirectHost:Ke,loopbackRedirect:Em.has(new URL(A).hostname),clientName:v.client_name||"Unnamed client",scopes:E,script:Sd}))},"authorizePage"),h=a((S,b,w)=>{c(b),Ld.default.json({limit:"4kb"})(S,b,()=>Ae(e,"api")(S,b,()=>{try{d();let v=/^\/mcp\/oauth\/consent\/([A-Za-z0-9_-]+)\/?$/.exec(S.path)?.[1]??"",A=i.get(v),E=A&&t.clientsStore.getClient(A.clientId);if(!A||!E){b.status(404).json({error:"Consent request expired or unknown"});return}let $=z(e.settings)&&S.user?.username||"owner",F=A.scopes.filter(Ft=>p(S).includes(Ft));if(!F.length){b.status(403).json({error:"Your Node-RED permissions do not allow granting the requested scopes",grantable:F});return}if(S.method==="GET"){A.approver=$,b.json({client_name:E.client_name??null,redirect_host:new URL(A.redirect).host,requested:A.scopes,grantable:F,user:$,csrf:A.csrf});return}if(S.method!=="POST"){b.status(405).end();return}let le=S.body??{};if(!f(S)||le.csrf!==A.csrf||A.approver!==$){b.status(403).json({error:"Consent request verification failed"});return}i.delete(v);let Ke=new URL(A.redirect);if(le.decision!=="allow")Ke.searchParams.set("error","access_denied");else{try{t.clientsStore.approve(E.client_id)}catch{b.status(409).json({error:"Maximum active OAuth clients reached"});return}let Ft=t.createCode(E,{redirectUri:A.redirect,codeChallenge:A.challenge,scopes:F,resource:new URL(A.resource)},$);Ke.searchParams.set("code",Ft)}A.state&&Ke.searchParams.set("state",A.state),b.json({redirect:Ke.href})}catch(v){w(v)}}))},"consentApi"),g=(0,Rd.clientRegistrationHandler)({clientsStore:t.clientsStore,rateLimit:{windowMs:36e5,max:10,standardHeaders:!0,legacyHeaders:!1}}),k=(0,Pd.tokenHandler)({provider:t}),T=(0,_d.revocationHandler)({provider:t}),C,L="";return{server:a((S,b,w)=>{let v=S.path,A=v.replace(/\/+$/,"").toLowerCase();if(A==="/mcp/oauth/authorize"){S.method==="GET"?m(S,b,w):b.status(405).set("Allow","GET").end();return}if(A.startsWith("/mcp/oauth/consent/")){h(S,b,w);return}if(!["/mcp/oauth/token","/mcp/oauth/register","/mcp/oauth/revoke"].includes(A)){w();return}let E=n();if(E.startsWith("https://")||/^http:\/\/(localhost|127\.0\.0\.1)(:|\/|$)/.test(E)){if(L!==E)try{C=(0,Cd.mcpAuthRouter)({provider:t,issuerUrl:new URL(E),baseUrl:new URL(`${E.replace(/\/$/,"")}/mcp/oauth/`),resourceServerUrl:new URL(r()),scopesSupported:fn,clientRegistrationOptions:{rateLimit:{windowMs:36e5,max:10}}}),L=E}catch{C=void 0}if(C){let $=S.url;S.url=S.url.slice(10)||"/",C(S,b,F=>{S.url=$,w(F)});return}}if(v==="/mcp/oauth/token"){s(k,S,b,w);return}if(v==="/mcp/oauth/register"){s(g,S,b,w);return}if(v==="/mcp/oauth/revoke"){s(T,S,b,w);return}w()},"server"),metadata:a((S,b,w)=>{let v=S.path;if(v==="/.well-known/oauth-authorization-server"){s((0,Es.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:fn}),S,b,w);return}if(v==="/.well-known/oauth-protected-resource/mcp"||v==="/.well-known/oauth-protected-resource"){s((0,Es.metadataHandler)({resource:lt(r()),authorization_servers:[n()],scopes_supported:fn}),S,b,w);return}w()},"metadata"),auth:a((S,b,w)=>{try{let v=b.set.bind(b);b.set=((A,E)=>(A==="WWW-Authenticate"&&typeof E=="string"&&(E=E.replace('scope="read"','scope="read propose"')),typeof A=="string"?v(A,E??""):v(A))),(0,Ed.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())})(S,b,A=>{b.set=v,w(A)})}catch(v){w(v)}},"auth")}}a(Nd,"makeOAuthHandlers");var Xn=require("fs"),hn=require("path"),Fd=require("url"),Od=require("yaml"),Nm={};function Cm(){return typeof __dirname<"u"?__dirname:(0,hn.dirname)((0,Fd.fileURLToPath)(Nm.url))}a(Cm,"moduleDir");function $d(){let e=Cm(),t=[(0,hn.join)(e,"..","docs","api","openapi.yaml"),(0,hn.join)(e,"..","..","docs","api","openapi.yaml"),(0,hn.join)(process.cwd(),"docs","api","openapi.yaml")];for(let n of t)if((0,Xn.existsSync)(n))return n;return null}a($d,"resolveOpenApiYamlPath");function Lm(){let e=$d();if(!e)throw new Error("OpenAPI specification file was not found");return(0,Xn.readFileSync)(e,"utf8")}a(Lm,"loadOpenApiYaml");function Md(){return(e,t)=>{try{let n=Lm();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(Md,"openApiYamlHandler");var Ns;function Im(){let e=$d();if(!e)throw new Error("OpenAPI specification file was not found");return Ns?.path!==e&&(Ns={path:e,json:JSON.stringify((0,Od.parse)((0,Xn.readFileSync)(e,"utf8")))}),Ns.json}a(Im,"loadOpenApiJson");function Dd(){return(e,t)=>{try{t.status(200).type("application/json").send(Im())}catch(n){let r=n instanceof Error?n.message:String(n);t.status(500).json({error:r})}}}a(Dd,"openApiJsonHandler");function Fm(){return`
1106
1109
  var settingsData = null;
1107
1110
  var settingsSaving = false;
1108
1111
  var settingsTimer = null;
@@ -1113,6 +1116,7 @@ a{color:var(--focus)}
1113
1116
  corsAllowedOrigins: 'cors', rateLimitWindowMs: 'rate-window',
1114
1117
  rateLimitMaxPerIp: 'rate-ip', rateLimitMaxPerToken: 'rate-token'
1115
1118
  };
1119
+ var SECRET_PATTERN_RE = /^[A-Za-z0-9_-]{2,40}$/;
1116
1120
  var OAUTH_ACCESS_UNITS = [['minutes', 60], ['hours', 3600]];
1117
1121
  var OAUTH_REFRESH_UNITS = [['hours', 3600], ['days', 86400]];
1118
1122
 
@@ -1180,6 +1184,7 @@ a{color:var(--focus)}
1180
1184
  if (badge) badge.hidden = !locked;
1181
1185
  });
1182
1186
  $('set-check-public-url').disabled = ro;
1187
+ renderSecretPatterns(data);
1183
1188
 
1184
1189
  var o = s.oauth || {};
1185
1190
  var oauthLocked = fieldLocked('oauth');
@@ -1236,7 +1241,95 @@ a{color:var(--focus)}
1236
1241
  return payload;
1237
1242
  }
1238
1243
 
1239
- `}a(ef,"settingsPartForm");function tf(){return`
1244
+ `}a(Fm,"settingsPartForm");function Om(){return`
1245
+ function secretPatternsEditable() {
1246
+ return settingsCanEdit() && !fieldLocked('secretNamePatterns');
1247
+ }
1248
+
1249
+ // Chips are built with createElement/textContent only (pattern text is never HTML).
1250
+ function renderSecretPatterns(data) {
1251
+ var list = $('secret-patterns');
1252
+ var s = (data && data.settings) || {};
1253
+ var patterns = Array.isArray(s.secretNamePatterns) ? s.secretNamePatterns : [];
1254
+ var editable = secretPatternsEditable();
1255
+ var source = data && data.sources ? data.sources.secretNamePatterns : 'default';
1256
+ $('secret-patterns-source').textContent = source === 'settings.js' ? '(set in settings.js)'
1257
+ : source === 'disk' ? '(customized)' : '(defaults)';
1258
+ while (list.firstChild) list.removeChild(list.firstChild);
1259
+ if (!patterns.length) {
1260
+ var empty = document.createElement('li');
1261
+ empty.className = 'chip-empty secret-patterns-empty-warning';
1262
+ empty.setAttribute('role', 'alert');
1263
+ empty.textContent = 'Warning: the pattern list is empty, so secrets are no longer redacted by ' +
1264
+ 'name (env vars, password/token fields). Only known token formats are still hidden from AI. ' +
1265
+ 'Use "Reset to defaults" to restore protection.';
1266
+ list.appendChild(empty);
1267
+ }
1268
+ patterns.forEach(function (p) {
1269
+ var li = document.createElement('li');
1270
+ li.className = 'chip';
1271
+ var label = document.createElement('code');
1272
+ label.textContent = String(p);
1273
+ li.appendChild(label);
1274
+ if (editable) {
1275
+ var rm = document.createElement('button');
1276
+ rm.type = 'button';
1277
+ rm.className = 'chip-remove';
1278
+ rm.setAttribute('aria-label', 'Remove ' + String(p));
1279
+ rm.title = 'Remove';
1280
+ rm.textContent = '\xD7';
1281
+ rm.addEventListener('click', function () {
1282
+ saveSecretPatterns(patterns.filter(function (x) { return x !== p; }));
1283
+ });
1284
+ li.appendChild(rm);
1285
+ }
1286
+ list.appendChild(li);
1287
+ });
1288
+ $('set-secret-new').disabled = !editable;
1289
+ $('set-secret-add').disabled = !editable;
1290
+ $('set-secret-reset').disabled = !editable || source !== 'disk';
1291
+ $('lock-secret-patterns').hidden = !fieldLocked('secretNamePatterns');
1292
+ }
1293
+
1294
+ function currentSecretPatterns() {
1295
+ var s = (settingsData && settingsData.settings) || {};
1296
+ return Array.isArray(s.secretNamePatterns) ? s.secretNamePatterns.slice() : [];
1297
+ }
1298
+
1299
+ function saveSecretPatterns(next) {
1300
+ if (!secretPatternsEditable() || settingsSaving) return;
1301
+ persistSettings({ secretNamePatterns: next });
1302
+ }
1303
+
1304
+ function addSecretPattern() {
1305
+ var input = $('set-secret-new');
1306
+ var value = String(input.value || '').trim().toUpperCase();
1307
+ if (!value) return;
1308
+ if (!SECRET_PATTERN_RE.test(value)) {
1309
+ settingsFlash('Pattern must be 2-40 letters, digits, "_" or "-".', false);
1310
+ return;
1311
+ }
1312
+ var list = currentSecretPatterns();
1313
+ if (list.indexOf(value) !== -1) {
1314
+ settingsFlash('Pattern ' + value + ' is already in the list.', false);
1315
+ return;
1316
+ }
1317
+ list.push(value);
1318
+ input.value = '';
1319
+ saveSecretPatterns(list);
1320
+ }
1321
+
1322
+ function resetSecretPatterns() {
1323
+ if (!secretPatternsEditable()) return;
1324
+ openConfirmDialog({
1325
+ title: 'Reset secret name patterns',
1326
+ message: 'Replace the list with the built-in defaults?',
1327
+ confirmLabel: 'Reset',
1328
+ onConfirm: function () { persistSettings({ secretNamePatterns: null }); }
1329
+ });
1330
+ }
1331
+
1332
+ `}a(Om,"settingsPartSecrets");function $m(){return`
1240
1333
  function settingsFlash(msg, ok) {
1241
1334
  var tab = document.querySelector('nav.tabs button[aria-selected="true"]');
1242
1335
  var target = tab && tab.getAttribute('data-tab') === 'oauth' ? 'oauth-flash' : 'settings-flash';
@@ -1324,7 +1417,7 @@ a{color:var(--focus)}
1324
1417
  }
1325
1418
  }
1326
1419
 
1327
- `}a(tf,"settingsPartSave");function nf(){return`
1420
+ `}a($m,"settingsPartSave");function Mm(){return`
1328
1421
  function editorNodeHref(c) {
1329
1422
  var base = editorRoot && editorRoot !== '/' ? editorRoot.replace(/\\/$/, '') + '/' : '/';
1330
1423
  if (c.nodeId) return base + '#node/' + encodeURIComponent(c.nodeId);
@@ -1425,7 +1518,7 @@ a{color:var(--focus)}
1425
1518
  }
1426
1519
  }
1427
1520
 
1428
- `}a(nf,"settingsPartOAuth");function rf(){return`
1521
+ `}a(Mm,"settingsPartOAuth");function Dm(){return`
1429
1522
  function loadOAuthTab() {
1430
1523
  loadSettings();
1431
1524
  loadOAuthStatus();
@@ -1438,7 +1531,7 @@ a{color:var(--focus)}
1438
1531
  [sec, oauthSec].forEach(function (host) {
1439
1532
  if (!host) return;
1440
1533
  host.querySelectorAll('input, select').forEach(function (el) {
1441
- if (el.id === 'set-public-url') return;
1534
+ if (el.id === 'set-public-url' || el.getAttribute('data-no-autosave')) return;
1442
1535
  el.addEventListener('change', scheduleSettingsSave);
1443
1536
  });
1444
1537
  });
@@ -1447,13 +1540,18 @@ a{color:var(--focus)}
1447
1540
  $('set-deploy-full-warn').hidden = $('set-deploy').value !== 'full';
1448
1541
  });
1449
1542
  $('set-check-public-url').addEventListener('click', checkPublicUrl);
1543
+ $('set-secret-add').addEventListener('click', addSecretPattern);
1544
+ $('set-secret-new').addEventListener('keydown', function (ev) {
1545
+ if (ev.key === 'Enter') { ev.preventDefault(); addSecretPattern(); }
1546
+ });
1547
+ $('set-secret-reset').addEventListener('click', resetSecretPatterns);
1450
1548
  $('oauth-refresh').addEventListener('click', function () { loadOAuthStatus(); loadOAuthClients(); });
1451
1549
  $('oauth-copy-url').addEventListener('click', function () {
1452
1550
  copyTextToClipboard($('oauth-server-url').textContent || '');
1453
1551
  showToast('Server URL copied.', true);
1454
1552
  });
1455
1553
  })();
1456
- `}a(rf,"settingsPartWire");function ja(){return ef()+tf()+nf()+rf()}a(ja,"pageClientSettingsScript");function sf(){return`
1554
+ `}a(Dm,"settingsPartWire");function jd(){return Fm()+Om()+$m()+Mm()+Dm()}a(jd,"pageClientSettingsScript");function Hm(){return`
1457
1555
  (function () {
1458
1556
  var root = document.getElementById('mcp-root');
1459
1557
  if (!root) return;
@@ -1674,7 +1772,7 @@ a{color:var(--focus)}
1674
1772
  }
1675
1773
  return body;
1676
1774
  }
1677
- `}a(sf,"pageClientScriptPartA");function af(){return`
1775
+ `}a(Hm,"pageClientScriptPartA");function Um(){return`
1678
1776
  // Time helpers \u2014 keep in sync with page-time.ts (always browser-local Intl).
1679
1777
  function titleFromDisplayName(displayName) {
1680
1778
  var s = String(displayName || '');
@@ -1781,7 +1879,7 @@ a{color:var(--focus)}
1781
1879
  btn.disabled = false;
1782
1880
  }, 1600);
1783
1881
  }
1784
- `}a(af,"pageClientScriptPartATimeCopy");function df(){return`
1882
+ `}a(Um,"pageClientScriptPartATimeCopy");function Bm(){return`
1785
1883
  function shortId(value, keep) {
1786
1884
  var s = String(value == null ? '' : value);
1787
1885
  var n = keep || 8;
@@ -1911,7 +2009,7 @@ a{color:var(--focus)}
1911
2009
  }
1912
2010
  return '';
1913
2011
  }
1914
- `}a(df,"pageClientScriptPartAFormat");function lf(){return`
2012
+ `}a(Bm,"pageClientScriptPartAFormat");function qm(){return`
1915
2013
  function lookupCachedProposal(id) {
1916
2014
  if (!id) return null;
1917
2015
  return cachedProposals.filter(function (x) { return x.id === id; })[0] || null;
@@ -2032,7 +2130,7 @@ a{color:var(--focus)}
2032
2130
  return k + '=' + String(v);
2033
2131
  }).join(', '));
2034
2132
  }
2035
- `}a(lf,"pageClientScriptPartAAudit");function cf(){return`
2133
+ `}a(qm,"pageClientScriptPartAAudit");function zm(){return`
2036
2134
  // Hash helpers \u2014 keep in sync with page-hash.ts
2037
2135
  var TAB_IDS = ['status', 'settings', 'links', 'oauth', 'proposals'];
2038
2136
  function hashForTab(tab) {
@@ -2318,7 +2416,7 @@ a{color:var(--focus)}
2318
2416
  }
2319
2417
  }
2320
2418
 
2321
- `}a(cf,"pageClientScriptPartATabs");function uf(){return`
2419
+ `}a(zm,"pageClientScriptPartATabs");function Wm(){return`
2322
2420
  async function loadLinks() {
2323
2421
  if (!aiOn || !canManage) {
2324
2422
  var dashLinksOff = $('dash-links');
@@ -2387,7 +2485,7 @@ a{color:var(--focus)}
2387
2485
  showToast(e.message || String(e), false);
2388
2486
  }
2389
2487
  }
2390
- `}a(uf,"pageClientScriptPartA2");function pf(){return`
2488
+ `}a(Wm,"pageClientScriptPartA2");function Gm(){return`
2391
2489
 
2392
2490
  function normalizeLinkKind(kind) {
2393
2491
  if (kind === 'rest' || kind === 'mcp' || kind === 'get_link') return kind;
@@ -2869,6 +2967,7 @@ a{color:var(--focus)}
2869
2967
  * Small add/modify-only edits without warnings stay one-click.
2870
2968
  */
2871
2969
  function needsApproveConfirm(diff, proposal) {
2970
+ if (proposal && Array.isArray(proposal.restoredSecrets) && proposal.restoredSecrets.length) return true;
2872
2971
  if (proposalWarnings(proposal, diff).length > 0) return true;
2873
2972
  if (!diff || !diff.summary) return false;
2874
2973
  var removed = Number(diff.summary.removed) || 0;
@@ -2887,6 +2986,41 @@ a{color:var(--focus)}
2887
2986
  return [];
2888
2987
  }
2889
2988
 
2989
+ /**
2990
+ * Fields where a [REDACTED] placeholder was restored to the live secret,
2991
+ * plus a strong banner when that node's destination or code changed.
2992
+ */
2993
+ var SECRET_WARN_CODES = ['secret_destination_changed', 'secret_node_code_changed',
2994
+ 'secret_wires_changed', 'secret_scope_changed', 'outbound_node_changed', 'secret_tab_changed',
2995
+ 'secret_reference_added'];
2996
+ function renderRestoredSecrets(p) {
2997
+ var fields = p && Array.isArray(p.restoredSecrets) ? p.restoredSecrets : [];
2998
+ var warns = (p && Array.isArray(p.warnings) ? p.warnings : []).filter(function (w) {
2999
+ return w && SECRET_WARN_CODES.indexOf(w.code) >= 0;
3000
+ });
3001
+ if (!fields.length && !warns.length) return '';
3002
+ var strong = warns.some(function (w) { return w.code !== 'secret_node_code_changed'; });
3003
+ var items = fields.map(function (f) {
3004
+ return '<li><code>' + esc(String(f.nodeId)) + '</code> \xB7 <code>' + esc(String(f.property)) + '</code></li>';
3005
+ }).join('');
3006
+ var warnItems = warns.map(function (w) {
3007
+ return '<li><strong>' + esc(String(w.code)) + '</strong> \u2014 ' + esc(String(w.error || '')) + '</li>';
3008
+ }).join('');
3009
+ return '<div class="proposal-warnings restored-secrets' + (strong ? ' restored-secrets-danger' : '') +
3010
+ '" role="' + (strong ? 'alert' : 'status') + '">' +
3011
+ '<p class="proposal-warnings-title">' +
3012
+ esc(strong
3013
+ ? 'Security review: this proposal changes what could send data (or secrets) elsewhere'
3014
+ : 'Secrets restored from the live flow (' + fields.length + ')') +
3015
+ '</p>' +
3016
+ (warnItems ? '<ul>' + warnItems + '</ul>' : '') +
3017
+ (fields.length
3018
+ ? '<p class="muted">The AI saw [REDACTED] in these fields; approving keeps the real values:</p>' +
3019
+ '<ul>' + items + '</ul>'
3020
+ : '') +
3021
+ '</div>';
3022
+ }
3023
+
2890
3024
  function renderValidationWarnings(warnings, opts) {
2891
3025
  if (!warnings || !warnings.length) return '';
2892
3026
  opts = opts || {};
@@ -3077,7 +3211,7 @@ a{color:var(--focus)}
3077
3211
  decided: Math.max(decidedTotal, cachedProposals.filter(function (p) { return p.status !== 'pending'; }).length)
3078
3212
  };
3079
3213
  }
3080
- `}a(pf,"pageClientScriptPartB");function ff(){return`
3214
+ `}a(Gm,"pageClientScriptPartB");function Km(){return`
3081
3215
  var DIFF_MODE_KEY = 'ai-access-diff-mode';
3082
3216
  var DIFF_SPLIT_MIN_PX = ${900};
3083
3217
  var PRESENCE_SHORT_TEXT_MAX = ${120};
@@ -3194,7 +3328,7 @@ a{color:var(--focus)}
3194
3328
  '</div>';
3195
3329
  }
3196
3330
 
3197
- `}a(ff,"pageClientScriptPartBDiff");function mf(){return`
3331
+ `}a(Km,"pageClientScriptPartBDiff");function Jm(){return`
3198
3332
  function isCollapsiblePresenceText(value) {
3199
3333
  var s = String(value == null ? '' : value);
3200
3334
  if (s.indexOf('\\n') >= 0) return true;
@@ -3295,7 +3429,7 @@ a{color:var(--focus)}
3295
3429
  });
3296
3430
  });
3297
3431
  }
3298
- `}a(mf,"pageClientScriptPartBDiffPresence");function hf(){return`
3432
+ `}a(Jm,"pageClientScriptPartBDiffPresence");function Vm(){return`
3299
3433
  function textChangeLineCount(ch) {
3300
3434
  return (ch.lines || []).length;
3301
3435
  }
@@ -3354,7 +3488,7 @@ a{color:var(--focus)}
3354
3488
  '<code class="diff-before" title="before">' + esc(JSON.stringify(ch.before)) + '</code>' +
3355
3489
  ' \u2192 <code class="diff-after" title="after">' + esc(JSON.stringify(ch.after)) + '</code></div>';
3356
3490
  }
3357
- `}a(hf,"pageClientScriptPartBDiffView");function gf(){return`
3491
+ `}a(Vm,"pageClientScriptPartBDiffView");function Ym(){return`
3358
3492
  function renderDiffSections(sections, mode, emptyMsg) {
3359
3493
  var parts = '';
3360
3494
  if (sections.added && sections.added.length) {
@@ -3442,7 +3576,7 @@ a{color:var(--focus)}
3442
3576
  // Full view kept for mode-toggle re-render inside .proposal-changes.
3443
3577
  return renderHumanDiff(diff, mode);
3444
3578
  }
3445
- `}a(gf,"pageClientScriptPartBDiffView2");function yf(){return`
3579
+ `}a(Ym,"pageClientScriptPartBDiffView2");function Xm(){return`
3446
3580
  function updatePendingBadges() {
3447
3581
  var counts = countPendingBuckets();
3448
3582
  var fullText = formatPendingBadgeText(counts.live, counts.stale);
@@ -3558,7 +3692,7 @@ a{color:var(--focus)}
3558
3692
  });
3559
3693
  });
3560
3694
  }
3561
- `}a(yf,"pageClientScriptPartB2");function wf(){return`
3695
+ `}a(Xm,"pageClientScriptPartB2");function Zm(){return`
3562
3696
  function renderDeleteBanner(diff) {
3563
3697
  if (!isLargeDelete(diff)) return '';
3564
3698
  var beforeN = diff.summary.beforeNodeCount || 0;
@@ -3640,7 +3774,7 @@ a{color:var(--focus)}
3640
3774
  loadProposals();
3641
3775
  loadLogs();
3642
3776
  }
3643
- `}a(wf,"pageClientScriptPartB2Actions");function bf(){return`
3777
+ `}a(Zm,"pageClientScriptPartB2Actions");function Qm(){return`
3644
3778
  function renderRequestedVia(p) {
3645
3779
  if (!p.createdBy) return '';
3646
3780
  if (p.createdBy.label) {
@@ -3700,9 +3834,9 @@ a{color:var(--focus)}
3700
3834
  var deleteBanner = cached ? renderDeleteBanner(cached) : '';
3701
3835
  // Card-level banner even before diff loads (warnings come from list API).
3702
3836
  var cardWarns = proposalWarnings(p, cached);
3703
- var warnBanner = cardWarns.length && !cached
3837
+ var warnBanner = renderRestoredSecrets(p) + (cardWarns.length && !cached
3704
3838
  ? renderValidationWarnings(cardWarns)
3705
- : '';
3839
+ : '');
3706
3840
  var stale = proposalIsStale(p);
3707
3841
  var actions = renderPendingActions(p, stale);
3708
3842
  var changes =
@@ -3728,7 +3862,7 @@ a{color:var(--focus)}
3728
3862
  actions +
3729
3863
  '</article>';
3730
3864
  }
3731
- `}a(bf,"pageClientScriptPartB2Cards");function vf(){return`
3865
+ `}a(Qm,"pageClientScriptPartB2Cards");function eh(){return`
3732
3866
  function renderStaleItem(p) {
3733
3867
  var cached = proposalDiffCache[p.id];
3734
3868
  var title = titleForProposalCard(p);
@@ -3903,7 +4037,7 @@ a{color:var(--focus)}
3903
4037
  bindProposalActions(list);
3904
4038
  focusProposalFromHash();
3905
4039
  }
3906
- `}a(vf,"pageClientScriptPartB2CardsList");function kf(){return`
4040
+ `}a(eh,"pageClientScriptPartB2CardsList");function th(){return`
3907
4041
  async function refreshFlowLabels() {
3908
4042
  try {
3909
4043
  var data = await api('/flows');
@@ -4070,7 +4204,7 @@ a{color:var(--focus)}
4070
4204
  replaceLocationHash('#proposals');
4071
4205
  return true;
4072
4206
  }
4073
- `}a(kf,"pageClientScriptPartB2Focus");function Af(){return`
4207
+ `}a(th,"pageClientScriptPartB2Focus");function nh(){return`
4074
4208
  function bindDiffModeToggle(host, diff) {
4075
4209
  host.querySelectorAll('[data-diff-mode]').forEach(function (btn) {
4076
4210
  btn.addEventListener('click', function () {
@@ -4182,7 +4316,7 @@ a{color:var(--focus)}
4182
4316
  esc(e.message || String(e)) + '</p>';
4183
4317
  }
4184
4318
  }
4185
- }`}a(Af,"pageClientScriptPartB2Apply");function xf(){return`
4319
+ }`}a(nh,"pageClientScriptPartB2Apply");function rh(){return`
4186
4320
 
4187
4321
  function dialogFocusables(dialog) {
4188
4322
  if (!dialog) return [];
@@ -4496,7 +4630,7 @@ a{color:var(--focus)}
4496
4630
  throw e;
4497
4631
  }
4498
4632
  }
4499
- `}a(xf,"pageClientScriptPartC");function Sf(){return`
4633
+ `}a(rh,"pageClientScriptPartC");function oh(){return`
4500
4634
  var cachedLogs = [];
4501
4635
 
4502
4636
  function renderLogsTable(logs) {
@@ -4573,7 +4707,7 @@ a{color:var(--focus)}
4573
4707
  showFlash(flash, e.message || String(e), false);
4574
4708
  }
4575
4709
  }
4576
- `}a(Sf,"pageClientScriptPartCLogs");function Tf(){return`
4710
+ `}a(oh,"pageClientScriptPartCLogs");function sh(){return`
4577
4711
  function applyStatus(s) {
4578
4712
  aiOn = !!s.aiFeaturesEnabled;
4579
4713
  canManage = !!(s.capabilities && s.capabilities.manageLinks);
@@ -4734,7 +4868,7 @@ a{color:var(--focus)}
4734
4868
  });
4735
4869
  }
4736
4870
  }
4737
- `}a(Tf,"pageClientScriptPartD");function Pf(){return`
4871
+ `}a(sh,"pageClientScriptPartD");function ih(){return`
4738
4872
  async function boot() {
4739
4873
  accessToken = readEditorToken();
4740
4874
  if (!accessToken) {
@@ -4840,7 +4974,7 @@ a{color:var(--focus)}
4840
4974
 
4841
4975
  boot();
4842
4976
  })();
4843
- `}a(Pf,"pageClientScriptPartDBoot");function Ha(){return(sf()+af()+df()+lf()+cf()+uf()+pf()+ff()+mf()+hf()+gf()+yf()+wf()+bf()+vf()+kf()+Af()+xf()+Sf()+Tf()+ja()+Pf()).trim()}a(Ha,"pageClientScript");function Rf(){return`
4977
+ `}a(ih,"pageClientScriptPartDBoot");function Hd(){return(Hm()+Um()+Bm()+qm()+zm()+Wm()+Gm()+Km()+Jm()+Vm()+Ym()+Xm()+Zm()+Qm()+eh()+th()+nh()+rh()+oh()+sh()+jd()+ih()).trim()}a(Hd,"pageClientScript");function ah(){return`
4844
4978
  :root {
4845
4979
  color-scheme: light;
4846
4980
  --bg: #eef2f5;
@@ -4910,7 +5044,7 @@ h1 {
4910
5044
  text-decoration: none;
4911
5045
  text-align: center;
4912
5046
  }
4913
- `}a(Rf,"pageStylesPartA");function _f(){return`
5047
+ `}a(ah,"pageStylesPartA");function dh(){return`
4914
5048
  nav.tabs {
4915
5049
  position: sticky;
4916
5050
  top: 0;
@@ -5004,7 +5138,7 @@ dd {
5004
5138
  padding: 0.22rem 0.55rem;
5005
5139
  border-radius: 6px;
5006
5140
  cursor: help;
5007
- background: var`}a(_f,"pageStylesPartA2");function Lf(){return`(--accent-soft);
5141
+ background: var`}a(dh,"pageStylesPartA2");function lh(){return`(--accent-soft);
5008
5142
  color: var(--accent);
5009
5143
  font-size: 0.82rem;
5010
5144
  font-weight: 650;
@@ -5195,7 +5329,7 @@ dialog.token-modal { width: min(720px, 94vw); max-width: min(720px, 94vw); max-h
5195
5329
  color: var(--muted);
5196
5330
  font-size: 0.92rem;
5197
5331
  }
5198
- `}a(Lf,"pageStylesPartB");function Cf(){return`
5332
+ `}a(lh,"pageStylesPartB");function ch(){return`
5199
5333
  .logs-toolbar { margin-bottom: 0.65rem; }
5200
5334
  table.audit-table {
5201
5335
  table-layout: fixed;
@@ -5271,7 +5405,7 @@ table.audit-table {
5271
5405
  cursor: help;
5272
5406
  border-bottom: 1px dotted var(--line);
5273
5407
  }
5274
- `}a(Cf,"pageStylesPartC");function Ef(){return`
5408
+ `}a(ch,"pageStylesPartC");function uh(){return`
5275
5409
  .proposal-diff {
5276
5410
  margin: 0.65rem 0;
5277
5411
  padding: 0.55rem 0.65rem;
@@ -5348,7 +5482,7 @@ table.audit-table {
5348
5482
  color: var(--muted);
5349
5483
  margin-bottom: 0.15rem;
5350
5484
  }
5351
- `}a(Ef,"pageStylesPartD");function If(){return`
5485
+ `}a(uh,"pageStylesPartD");function ph(){return`
5352
5486
  .diff-block {
5353
5487
  margin-top: 0.25rem;
5354
5488
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
@@ -5430,7 +5564,7 @@ table.audit-table {
5430
5564
  }
5431
5565
  .diff-before { color: var(--danger); word-break: break-word; }
5432
5566
  .diff-after { color: var(--ok); word-break: break-word; }
5433
- `}a(If,"pageStylesPartE");function Nf(){return`
5567
+ `}a(ph,"pageStylesPartE");function fh(){return`
5434
5568
  .proposal-detail {
5435
5569
  scroll-margin-top: 4.5rem;
5436
5570
  }
@@ -5556,7 +5690,7 @@ table.audit-table {
5556
5690
  .audit-table .col-action { width: 5.5rem; min-width: 5.5rem; }
5557
5691
  .audit-table .col-actor { width: 7rem; min-width: 6rem; }
5558
5692
  }
5559
- `}a(Nf,"pageStylesPartF");function Ff(){return`
5693
+ `}a(fh,"pageStylesPartF");function mh(){return`
5560
5694
  .dash-grid {
5561
5695
  display: grid;
5562
5696
  grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
@@ -5668,7 +5802,18 @@ button.id-copy:focus {
5668
5802
  font-weight: 650;
5669
5803
  font-size: 0.92rem;
5670
5804
  }
5671
- .proposal-warnings {
5805
+ .secret-patterns-empty-warning {
5806
+ list-style: none;
5807
+ color: #b42318;
5808
+ font-weight: 600;
5809
+ }
5810
+ .restored-secrets-danger {
5811
+ border-color: #b42318 !important;
5812
+ background: #fef3f2 !important;
5813
+ color: #7a271a;
5814
+ }
5815
+ .restored-secrets-danger .proposal-warnings-title { color: #b42318; font-weight: 700; }
5816
+ .proposal-warnings {
5672
5817
  margin: 0 0 0.55rem;
5673
5818
  padding: 0.55rem 0.7rem;
5674
5819
  background: #fff8e8;
@@ -5773,7 +5918,7 @@ button.id-copy:focus {
5773
5918
  background: var(--code-bg, var(--soft));
5774
5919
  border-radius: 4px;
5775
5920
  }
5776
- `}a(Ff,"pageStylesPartG");function Of(){return`
5921
+ `}a(mh,"pageStylesPartG");function hh(){return`
5777
5922
  .stale-group {
5778
5923
  margin: 1rem 0 0;
5779
5924
  border: 1px solid var(--warn-border);
@@ -5937,7 +6082,7 @@ dialog h3 { margin: 0 0 0.4rem; }
5937
6082
  margin-left: auto;
5938
6083
  }
5939
6084
  .debug-toggle input { width: auto; }
5940
- `}a(Of,"pageStylesPartH");function $f(){return`
6085
+ `}a(hh,"pageStylesPartH");function gh(){return`
5941
6086
  .card {
5942
6087
  background: var(--card);
5943
6088
  border: 1px solid var(--line);
@@ -5974,6 +6119,18 @@ dialog h3 { margin: 0 0 0.4rem; }
5974
6119
  font-size: 0.8rem; color: var(--muted); border: 1px solid var(--line);
5975
6120
  border-radius: 999px; padding: 0.1rem 0.5rem; white-space: nowrap;
5976
6121
  }
6122
+ .chip-list { list-style: none; display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.5rem 0 0.6rem; padding: 0; }
6123
+ .chip {
6124
+ display: inline-flex; align-items: center; gap: 0.25rem; padding: 0.15rem 0.25rem 0.15rem 0.55rem;
6125
+ border: 1px solid var(--line); border-radius: 999px; background: var(--soft); font-size: 0.85rem;
6126
+ }
6127
+ .chip code { background: none; padding: 0; }
6128
+ .chip-remove {
6129
+ border: 0; background: transparent; color: var(--muted); cursor: pointer; font-size: 1rem;
6130
+ line-height: 1; padding: 0.1rem 0.35rem; border-radius: 999px;
6131
+ }
6132
+ .chip-remove:hover, .chip-remove:focus-visible { color: var(--danger); background: #fdecec; }
6133
+ .chip-empty { font-size: 0.88rem; }
5977
6134
  .notice { padding: 0.6rem 0.8rem; border-radius: 8px; background: var(--soft); border: 1px solid var(--line); }
5978
6135
  .check-result { margin: 0.6rem 0 0; padding: 0.6rem 0.8rem; border-radius: 8px; font-weight: 600; overflow-wrap: anywhere; }
5979
6136
  .check-result.is-ok { color: var(--ok); background: #e8f6ef; border: 1px solid #a8d8b4; }
@@ -5995,7 +6152,7 @@ dialog h3 { margin: 0 0 0.4rem; }
5995
6152
  nav.tabs button { flex: 0 0 auto; }
5996
6153
  .field-line { flex-wrap: wrap; }
5997
6154
  }
5998
- `}a($f,"pageStylesDashboard");function Ua(){return(Rf()+_f()+Lf()+Cf()+Ef()+If()+Nf()+Ff()+Of()+$f()).trim()}a(Ua,"pageStyles");function Mf(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;")}a(Mf,"escapeHtml");function Df(){return`
6155
+ `}a(gh,"pageStylesDashboard");function Ud(){return(ah()+dh()+lh()+ch()+uh()+ph()+fh()+mh()+hh()+gh()).trim()}a(Ud,"pageStyles");function yh(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;")}a(yh,"escapeHtml");function wh(){return`
5999
6156
  <header class="page-header">
6000
6157
  <h1>Node-RED AI Access</h1>
6001
6158
  <p class="lede">
@@ -6026,7 +6183,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6026
6183
  Proposals <span class="tab-badge" id="proposals-pending-badge" hidden>0</span>
6027
6184
  </button>
6028
6185
  <button type="button" data-tab="logs" aria-selected="false">Audit log</button>
6029
- </nav>`}a(Df,"renderMcpPageHeader");function jf(){return`
6186
+ </nav>`}a(wh,"renderMcpPageHeader");function bh(){return`
6030
6187
  <section class="panel active" id="section-status" aria-label="Overview" hidden>
6031
6188
  <h2>Overview</h2>
6032
6189
  <div id="status-flash" class="flash" hidden></div>
@@ -6070,7 +6227,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6070
6227
  <div class="row" style="margin-top:1rem">
6071
6228
  <button type="button" class="action secondary" id="refresh-status">Refresh</button>
6072
6229
  </div>
6073
- </section>`}a(jf,"renderMcpPageStatusSection");function Hf(){return`
6230
+ </section>`}a(bh,"renderMcpPageStatusSection");function vh(){return`
6074
6231
  <section class="panel" id="section-links" aria-label="Access tokens" hidden>
6075
6232
  <h2>Access tokens</h2>
6076
6233
  <p class="muted" id="links-intro">
@@ -6081,7 +6238,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6081
6238
  <div id="links-flash" class="flash" hidden></div>
6082
6239
  <div id="links-manage"></div>
6083
6240
  <div id="links-list"></div>
6084
- </section>`}a(Hf,"renderMcpPageLinksSection");function Pe(e){return`<span class="lock-badge" id="lock-${e}" hidden>Locked by settings.js</span>`}a(Pe,"lockBadge");function Uf(){return`
6241
+ </section>`}a(vh,"renderMcpPageLinksSection");function be(e){return`<span class="lock-badge" id="lock-${e}" hidden>Locked by settings.js</span>`}a(be,"lockBadge");function kh(){return`
6085
6242
  <section class="panel" id="section-settings" aria-label="Settings" hidden>
6086
6243
  <h2>Settings</h2>
6087
6244
  <p class="muted">Changes save automatically and apply immediately (no restart).</p>
@@ -6094,13 +6251,13 @@ dialog h3 { margin: 0 0 0.4rem; }
6094
6251
  <label class="switch-label"><input type="checkbox" id="set-enabled" />
6095
6252
  <span><strong>Enable AI Access features</strong>
6096
6253
  <span class="hint">When off, access tokens, proposals, and AI tools are disabled.</span></span></label>
6097
- ${Pe("enabled")}
6254
+ ${be("enabled")}
6098
6255
  </div>
6099
6256
  <div class="form-row toggle-row" id="row-allow-without" hidden>
6100
6257
  <label class="switch-label"><input type="checkbox" id="set-allow-without" />
6101
6258
  <span><strong>Allow AI Access without adminAuth (local-only)</strong>
6102
6259
  <span class="hint danger-text">Not for production. Anyone who can reach the editor admin port can use AI features.</span></span></label>
6103
- ${Pe("allow-without")}
6260
+ ${be("allow-without")}
6104
6261
  </div>
6105
6262
  <div class="form-row">
6106
6263
  <label for="set-deploy">Default deploy type</label>
@@ -6110,7 +6267,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6110
6267
  <option value="flows">Changed flows</option>
6111
6268
  <option value="full">Full redeploy</option>
6112
6269
  </select>
6113
- ${Pe("deploy")}
6270
+ ${be("deploy")}
6114
6271
  </div>
6115
6272
  <p class="hint danger-text" id="set-deploy-full-warn" hidden>Full redeploy restarts the entire runtime.</p>
6116
6273
  </div>
@@ -6119,7 +6276,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6119
6276
  <div class="field-line">
6120
6277
  <input type="number" id="set-ttl" min="1" step="1" class="num" />
6121
6278
  <span class="unit">hours</span>
6122
- ${Pe("ttl")}
6279
+ ${be("ttl")}
6123
6280
  </div>
6124
6281
  </div>
6125
6282
  <div class="form-row">
@@ -6127,7 +6284,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6127
6284
  <div class="field-line">
6128
6285
  <input type="number" id="set-max-links" min="1" step="1" class="num" />
6129
6286
  <span class="unit">tokens</span>
6130
- ${Pe("max-links")}
6287
+ ${be("max-links")}
6131
6288
  </div>
6132
6289
  </div>
6133
6290
  </div>
@@ -6140,7 +6297,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6140
6297
  <div class="field-line">
6141
6298
  <input type="url" id="set-public-url" placeholder="https://nodered.example.com" class="grow" />
6142
6299
  <button type="button" class="action secondary" id="set-check-public-url">Check</button>
6143
- ${Pe("public-url")}
6300
+ ${be("public-url")}
6144
6301
  </div>
6145
6302
  <div id="set-public-url-warning" class="hint warn-text" role="status" hidden></div>
6146
6303
  <div id="set-check-result" class="check-result" aria-live="polite" hidden></div>
@@ -6158,8 +6315,24 @@ dialog h3 { margin: 0 0 0.4rem; }
6158
6315
  <label for="set-cors">Allowed origins <span class="hint">(comma separated)</span></label>
6159
6316
  <div class="field-line">
6160
6317
  <input type="text" id="set-cors" class="grow" placeholder="https://app.example.com" />
6161
- ${Pe("cors")}
6318
+ ${be("cors")}
6319
+ </div>
6320
+ </div>
6321
+ </div>
6322
+
6323
+ <div class="card settings-card" id="secret-redaction-card">
6324
+ <h3>Secret redaction</h3>
6325
+ <p class="hint">Values whose names match these words are shown to AI clients as <code>[REDACTED]</code>: env variables (flow, group, subflow, node) and secret-named node settings. Known token formats (GitHub, AWS, Slack, OpenAI, Google, JWT, private keys, Bearer headers, URL passwords) are always redacted. Matching is case-insensitive on whole words, so <code>KEY</code> matches <code>API_KEY</code> but not <code>KEYBOARD</code>. The dashboard always shows real values, and proposals keep secrets the AI left as <code>[REDACTED]</code>.</p>
6326
+ <div class="form-row">
6327
+ <span class="label" id="secret-patterns-label">Secret name patterns <span class="hint" id="secret-patterns-source"></span></span>
6328
+ <ul class="chip-list" id="secret-patterns" aria-labelledby="secret-patterns-label"></ul>
6329
+ <div class="field-line">
6330
+ <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" />
6331
+ <button type="button" class="action secondary" id="set-secret-add">Add</button>
6332
+ <button type="button" class="action secondary" id="set-secret-reset">Reset to defaults</button>
6333
+ ${be("secret-patterns")}
6162
6334
  </div>
6335
+ <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>
6163
6336
  </div>
6164
6337
  </div>
6165
6338
 
@@ -6170,7 +6343,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6170
6343
  <div class="field-line">
6171
6344
  <input type="number" id="set-rate-window" min="1" step="1" class="num" />
6172
6345
  <span class="unit">ms</span>
6173
- ${Pe("rate-window")}
6346
+ ${be("rate-window")}
6174
6347
  </div>
6175
6348
  </div>
6176
6349
  <div class="form-row">
@@ -6178,7 +6351,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6178
6351
  <div class="field-line">
6179
6352
  <input type="number" id="set-rate-ip" min="1" step="1" class="num" />
6180
6353
  <span class="unit">requests / window</span>
6181
- ${Pe("rate-ip")}
6354
+ ${be("rate-ip")}
6182
6355
  </div>
6183
6356
  </div>
6184
6357
  <div class="form-row">
@@ -6186,13 +6359,13 @@ dialog h3 { margin: 0 0 0.4rem; }
6186
6359
  <div class="field-line">
6187
6360
  <input type="number" id="set-rate-token" min="1" step="1" class="num" />
6188
6361
  <span class="unit">requests / window</span>
6189
- ${Pe("rate-token")}
6362
+ ${be("rate-token")}
6190
6363
  </div>
6191
6364
  </div>
6192
6365
  <p class="hint" id="set-trust-proxy-inline"></p>
6193
6366
  </div>
6194
6367
  </div>
6195
- </section>`}a(Uf,"renderMcpPageSettingsSection");function Bf(){return`
6368
+ </section>`}a(kh,"renderMcpPageSettingsSection");function Ah(){return`
6196
6369
  <section class="panel" id="section-oauth" aria-label="OAuth" hidden>
6197
6370
  <h2>MCP OAuth</h2>
6198
6371
  <p class="muted">MCP clients (ChatGPT, Claude, \u2026) can request read or propose access through OAuth.</p>
@@ -6205,7 +6378,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6205
6378
  <label class="switch-label"><input type="checkbox" id="oauth-enabled" />
6206
6379
  <span><strong>Enable MCP OAuth</strong>
6207
6380
  <span class="hint">Also available in the editor sidebar (AI Access tab).</span></span></label>
6208
- ${Pe("oauth")}
6381
+ ${be("oauth")}
6209
6382
  </div>
6210
6383
  <div class="form-row">
6211
6384
  <span class="label">Server URL for MCP clients</span>
@@ -6267,7 +6440,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6267
6440
  <button type="button" class="action secondary" id="oauth-refresh">Refresh</button></div>
6268
6441
  <div id="oauth-clients" aria-live="polite"><p class="muted">Loading\u2026</p></div>
6269
6442
  </div>
6270
- </section>`}a(Bf,"renderMcpPageOAuthSection");function qf(){return`
6443
+ </section>`}a(Ah,"renderMcpPageOAuthSection");function Sh(){return`
6271
6444
  <section class="panel" id="section-proposals" aria-label="Proposals" hidden>
6272
6445
  <h2>Proposals</h2>
6273
6446
  <p id="proposals-hint" class="muted"></p>
@@ -6285,7 +6458,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6285
6458
  </div>
6286
6459
  <div id="proposals-flash" class="flash" hidden></div>
6287
6460
  <div id="proposals-list"><p class="muted">Loading\u2026</p></div>
6288
- </section>`}a(qf,"renderMcpPageProposalsSection");function Wf(){return`
6461
+ </section>`}a(Sh,"renderMcpPageProposalsSection");function xh(){return`
6289
6462
  <section class="panel" id="section-logs" aria-label="Audit log" hidden>
6290
6463
  <h2>Audit log</h2>
6291
6464
  <div id="logs-flash" class="flash" hidden></div>
@@ -6301,7 +6474,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6301
6474
  </label>
6302
6475
  </div>
6303
6476
  <div id="logs-list"><p class="muted">Loading\u2026</p></div>
6304
- </section>`}a(Wf,"renderMcpPageLogsSection");function zf(){return`
6477
+ </section>`}a(xh,"renderMcpPageLogsSection");function Th(){return`
6305
6478
  <dialog id="reject-dialog">
6306
6479
  <form method="dialog" id="reject-form">
6307
6480
  <h3>Reject proposal</h3>
@@ -6343,7 +6516,7 @@ dialog h3 { margin: 0 0 0.4rem; }
6343
6516
  <button type="submit" value="confirm" class="action danger" id="confirm-action-btn">Confirm</button>
6344
6517
  </div>
6345
6518
  </form>
6346
- </dialog>`}a(zf,"renderMcpPageDialogs");function Ba(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>
6519
+ </dialog>`}a(Th,"renderMcpPageDialogs");function Bd(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>
6347
6520
  <html lang="en">
6348
6521
  <head>
6349
6522
  <meta charset="utf-8" />
@@ -6351,23 +6524,23 @@ dialog h3 { margin: 0 0 0.4rem; }
6351
6524
  <meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" />
6352
6525
  <meta http-equiv="Pragma" content="no-cache" />
6353
6526
  <title>Node-RED AI Access</title>
6354
- <style>${Ua()}</style>
6527
+ <style>${Ud()}</style>
6355
6528
  </head>
6356
6529
  <body>
6357
6530
  <main
6358
6531
  id="mcp-root"
6359
- data-config="${Mf(JSON.stringify(t))}"
6360
- >${Df()}
6361
- ${jf()}
6362
- ${Uf()}
6363
- ${Hf()}
6364
- ${Bf()}
6365
- ${qf()}
6366
- ${Wf()}
6367
- ${zf()}
6532
+ data-config="${yh(JSON.stringify(t))}"
6533
+ >${wh()}
6534
+ ${bh()}
6535
+ ${kh()}
6536
+ ${vh()}
6537
+ ${Ah()}
6538
+ ${Sh()}
6539
+ ${xh()}
6540
+ ${Th()}
6368
6541
  </main>
6369
- <script>${Ha()}</script>
6542
+ <script>${Hd()}</script>
6370
6543
  </body>
6371
- </html>`}a(Ba,"renderMcpPage");Ae();ve();Ge();G();it();K();Fe();Xe();Ae();function Tt(e,t,n,r={}){e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("Content-Type","application/json; charset=utf-8");let o={error:Oe[n]??"Request failed",code:n,...r},s=se(n,"rest");s&&typeof o.hint!="string"&&(o.hint=s),e.status(t).json(o)}a(Tt,"sendSafeJsonError");function qa(e){let t={kind:"admin"};return typeof e.user?.username=="string"&&(t.username=e.user.username),t}a(qa,"adminActor");function Wa(e,t,n,r,o){return async(s,i)=>{if(!s.accessLink){i.setHeader("WWW-Authenticate",Y),i.status(401).json({error:le,code:Le});return}try{let d=q(s,r),l=re(s.query),c=await Ye(e,t,s.accessLink,s.body??{},n,d,o),u=V(e,c.expectedRev,{flowId:c.payload.flowId}),p=Ie(e,e.get(c.id),s.accessLink.id),f=c.warnings;i.status(201).json({...ce(c,{...d,include:l}),mine:!0,...Ne(p),...Q(u),...f&&f.length>0?{warnings:f}:{}})}catch(d){let l=O(d,"rest");if(l){i.status(l.statusCode).json(l.body);return}$(i,d);return}}}a(Wa,"createProposalHandler");function za(e,t,n){return async(r,o)=>{let s,i;try{let S=await t.getFlows();s=S.rev,i=dr(S.flows)}catch{}let d={...q(r,n),...i?{liveFlowLabels:i}:{}},l=re(r.query),c;try{c=mr(r.query)}catch(S){$(o,S);return}let u=s?V(e,s):0,p=r.accessLink;if(p){let S=qt(e,p,d,c),E=S.proposals.map(I=>{let N=ue(e,p,I,{...d,include:l},{includeConflicts:!1});return N.status!=="pending"||s===void 0?N:{...N,baseRevStale:N.expectedRev!==s}});o.json({proposals:E,...ft({...S,returned:S.proposals.length}),...Q(u)});return}let f={};c.flowId&&(f.flowId=c.flowId);let m=Ys(e.list(f).filter(S=>Co(S.status,c.status)),{...d,include:l}),h=c.offset??0,x=(c.limit!==void 0?m.slice(h,h+c.limit):h>0?m.slice(h):m).map(S=>S.status!=="pending"||s===void 0?S:{...S,baseRevStale:S.expectedRev!==s});o.json({proposals:x,...ft({total:m.length,limit:c.limit??null,offset:h,returned:x.length}),...Q(u)})}}a(za,"listProposalsHandler");function Ga(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=q(r,t),d=re(r.query),l;if(n)try{l=(await n.getFlows()).rev}catch{}try{if(r.accessLink){let c=De(e,r.accessLink,s,i);o.json(ue(e,r.accessLink,c,{...i,include:d},{...l?{liveRev:l}:{}}));return}l&&V(e,l),o.json(ce(e.get(s),{...i,include:d}))}catch(c){if(c instanceof ne){Tt(o,404,c.code);return}let u=O(c,"rest");if(u){o.status(u.statusCode).json(u.body);return}$(o,c);return}}}a(Ga,"getProposalHandler");function Ja(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.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,qa(n),i);r.json(oe(d,q(n,t)))}catch(d){if(d instanceof ne||d instanceof te||d instanceof kn||d instanceof X){Tt(r,d.statusCode,d.code);return}$(r,d);return}}}a(Ja,"approveProposalHandler");function Ka(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=oe(e.reject(o,qa(n),i),q(n,t));r.json(d)}catch(d){if(d instanceof ne||d instanceof te||d instanceof X){Tt(r,d.statusCode,d.code);return}$(r,d);return}}}a(Ka,"rejectProposalHandler");function Va(e,t,n){return(r,o)=>{if(!r.accessLink){o.setHeader("WWW-Authenticate",Y),o.status(401).json({error:le,code:Le});return}let s=r.params.id;if(typeof s!="string"||s.length===0){o.status(400).json({error:"Missing proposal id",code:"missing_proposal_id"});return}let i=r.body??{},d=q(r,n),l=re(r.query);try{let c=Wt(e,t,r.accessLink,s,i.reason,d);o.json(ue(e,r.accessLink,c,{...d,include:l}))}catch(c){let u=O(c,"rest");if(u){Tt(o,u.statusCode,u.body.code);return}Tt(o,500,"internal_error");return}}}a(Va,"withdrawProposalHandler");function Xa(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?De(e,n.accessLink,o):e.get(o),i=Yt(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=Qt(await nn(s,t),i.format);r.json(d)}catch(s){if(s instanceof ne){Tt(r,404,s.code);return}if(s instanceof z||s instanceof be||s instanceof Ce){Tt(r,s.statusCode,s.code);return}let i=O(s,"rest");if(i){r.status(i.statusCode).json(i.body);return}$(r,s);return}}}a(Xa,"getProposalDiffHandler");ve();ve();or();function Gf(e){return e.endsWith("/")?e.slice(0,-1):e}a(Gf,"trimTrailingSlash");var Wr=class{static{a(this,"HttpAdapter")}kind="http";baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=Gf(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 z(n):t.status===409?new J("unknown","unknown",`Admin API ${n}`):t.status===401||t.status===403?new Ce(Qn):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 An(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 z(t);let s=await this.readJson(r,"GET /flows"),i=await this.readJson(o,`GET /flow/${t}`);return{rev:An(s,"GET /flows").rev,flow:$t(t,i)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new J(t.expectedRev,r.rev,"updateFlow");let o=rr(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 J(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}}};ve();or();function Jf(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(Jf,"requireFlowsApi");function Kf(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(Kf,"resolveRev");var zr=class{static{a(this,"RedAdapter")}kind="red";flows;constructor(t){this.flows=Jf(t)}async getFlows(){let t=await this.flows.getFlows({});return An(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 Vf(o)?new z(t):o}return{rev:n.rev,flow:$t(t,r)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new J(t.expectedRev,r.rev,"updateFlow");let o=rr(r.flows,t.flowId,t.flow),s=await this.flows.setFlows({flows:{flows:o,rev:t.expectedRev},deploymentType:n});return{flowId:t.flowId,rev:Kf(s)}}};function Ya(e){let t=e?.runtime?.flows;return!!(t?.getFlows&&t.getFlow&&t.setFlows)}a(Ya,"canUseRedRuntime");function Vf(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(Vf,"isRuntimeFlowNotFound");var Qa=require("module");function Za(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(Za,"isFlowsApi");function Xf(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Qa.createRequire)(e)}a(Xf,"createPluginRequire");function Yf(e){try{let t=e("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}a(Yf,"isNodeRedRuntimeInitialized");function Qf(){try{let e=Xf();if(!Yf(e))return null;let t=e("@node-red/runtime");return Za(t.flows)?t.flows:null}catch{return null}}a(Qf,"tryRequireInitializedRuntimeFlows");function ed(e){if(e?.runtime&&Za(e.runtime.flows))return e;let t=Qf();return t?{runtime:{flows:t}}:null}a(ed,"resolveRuntimeHost");function td(e={}){let t=ed(e.RED??null);if(t&&Ya(t))return new zr(t);let n=e.adminAuthEnabled===!0;if(e.http?.baseUrl){if(n&&!e.http.accessToken)throw new be("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 Wr(e.http)}throw new be("No usable Node-RED flow adapter: in-process flows API is unavailable and no HTTP Admin API baseUrl was provided")}a(td,"createFlowAdapter");ve();var Jr=require("fs"),nd=require("path");er();G();var Zf=/^[a-z][a-z0-9._-]{0,63}$/;function em(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(em,"sanitizeActor");var Gr=class{constructor(t,n={}){this.paths=t;this.filePath=(0,nd.join)(t.logs,"audit.jsonl"),this.now=n.now??(()=>Date.now()),this.maxAppendBytes=n.maxAppendBytes??cs}static{a(this,"AuditLog")}filePath;now;maxAppendBytes;seq=0;append(t,n,r){let o=Zf.test(t)?t:"unknown";this.seq+=1;let s={id:`log-${this.now()}-${this.seq}`,at:new Date(this.now()).toISOString(),action:o,actor:em(n)};r!==void 0&&(s.details=_s(r));let i=Be(s,this.maxAppendBytes,{kind:"audit"});return ps(this.paths.root,this.filePath,i),s}list(t=100){if(!(0,Jr.existsSync)(this.filePath))return[];let n=(0,Jr.readFileSync)(this.filePath,"utf8").split(`
6372
- `).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()}};Ge();Gn();ie();ut();ve();Ge();Xe();Ae();var tm="Flow revision conflict: the expectedRev is stale. Ask the AI to re-read flows and resubmit a new proposal.";function rd(e){let t=oe(e),n=cr(e)||void 0;return{proposalId:e.id,title:t.title,displayName:t.displayName,flowId:e.payload.flowId,...n?{flowLabel:n}:{}}}a(rd,"auditProposalMeta");var Kr=class{constructor(t,n,r,o){this.proposals=t;this.audit=n;this.adapter=r;this.getDeploymentType=o}static{a(this,"ProposalService")}async approve(t,n,r={}){let o=this.proposals.beginApprove(t),s=rd(o),i=r.deploymentType??this.getDeploymentType();try{let d=o.type;if(d!=="replace_flow"){let l=this.proposals.markFailed(t,n,`Unsupported proposal type '${d}'`);return this.audit.append("proposal.failed",n,{...s,reason:l.reason}),l}try{let l=await this.adapter.updateFlow({flowId:o.payload.flowId,flow:o.payload.flow,expectedRev:o.expectedRev,deploymentType:i}),c=this.proposals.markApproved(t,n,l.rev);return this.audit.append("proposal.approved",n,{...s,resultRev:l.rev,flowId:o.payload.flowId}),ei(this.proposals,o.payload.flowId,t,l.rev),c}catch(l){if(l instanceof J){let p=this.proposals.markRejected(t,n,tm);return this.audit.append("proposal.rejected",n,{...s,reason:p.reason,code:l.code,expectedRev:l.expectedRev,currentRev:l.currentRev}),p}let c=l instanceof Error?l.message:String(l),u=this.proposals.markFailed(t,n,c);return this.audit.append("proposal.failed",n,{...s,reason:c}),u}}finally{this.proposals.releaseLock(t)}}reject(t,n,r){let o=this.proposals.get(t);if(o.status!=="pending")throw new te(t,o.status);let s=rd(o),i=this.proposals.markRejected(t,n,r.trim()||"Rejected by admin");return this.audit.append("proposal.rejected",n,{...s,reason:i.reason}),i}};ye();function nm(e){let t=a(()=>Promise.reject(new Ce(e)),"fail");return{kind:"http",getFlows:t,getFlow:t,updateFlow:t}}a(nm,"misconfiguredAdapter");function od(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(od,"localAdminBaseUrl");function Vr(e,t={}){let n=Bn(e.settings.userDir);fs(n);let r=t.settingsStore??new Lt(n,e.settings.aiAccess,u=>e.log?.warn?.(u));t.bindSettings!==!1&&Jn(r),Vn(r.getEffective().publicBaseUrl);let o=t.linkStore??Ur(e);o.configureDefaults({defaultTtlSeconds:r.getEffective().defaultLinkTtlSeconds,maxActiveLinks:r.getEffective().maxActiveLinks});let s=new tr(n),i=new Gr(n),d;if(t.flowAdapter)d=t.flowAdapter;else try{d=td({RED:e,http:{baseUrl:od(e)},adminAuthEnabled:W(e.settings)})}catch(u){let p=u instanceof be?u.message:Qn;e.log?.warn?.(`AI Access flow adapter unavailable: ${p}`),d=nm(p)}let l;if(t.nodeTypesAdapter)l=t.nodeTypesAdapter;else if(t.flowAdapter)l=ho();else try{l=Hs({baseUrl:od(e)})}catch(u){let p=u instanceof Error?u.message:String(u);e.log?.warn?.(`AI Access node-types adapter unavailable: ${p}`),l=ho([])}let c=new Kr(s,i,d,()=>he(e.settings).defaultDeploymentType);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(Vr,"createPluginServices");G();ie();_e();qn();var sd=ln(require("dns"),1),id=ln(require("http"),1),ad=ln(require("https"),1),Dn=require("net");ie();function rm(e,t){return e===10||e===172&&t>=16&&t<=31||e===192&&t===168}a(rm,"isPrivateV4");function om(e,t){return e===169&&t===254}a(om,"isLinkLocalV4");function sm(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(sm,"isSpecialUseV4");function im(e){let[t=0,n=0,r=0]=e.split(".").map(Number);return t===127?"loopback":rm(t,n)||om(t,n)||sm(t,n,r)||e==="168.63.129.16"?"blocked":"public"}a(im,"classifyIpv4");function am(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(am,"expandIpv6");function dm(e){let t=am(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(dm,"classifyIpv6");function jn(e){let t=at(e);return t?(0,Dn.isIP)(t)===4?im(t):dm(t):"blocked"}a(jn,"classifyPublicUrlAddress");function lm(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(lm,"isMetadataHostname");async function dd(e,t,n){let r=Ct(e);if(!r)throw new Error("publicBaseUrl is required for Check");let o=new URL(r),s=o.hostname.replace(/^\[|\]$/g,"");if(lm(s))throw new Error("Public URL Check cannot target metadata hosts");let i=at(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&&jn(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 x=jn(g);if(x==="blocked"||x==="loopback"&&!c||c&&x!=="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,Dn.isIP)(i)};let f=await new Promise((g,x)=>{let S=a(()=>x(n.reason instanceof Error?n.reason:new Error(String(n.reason))),"abort");n.throwIfAborted(),n.addEventListener("abort",S,{once:!0}),sd.default.lookup(s,{all:!0},(E,I)=>{n.removeEventListener("abort",S),E?x(E):g(I)})});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 x=at(g.address);if(!x)throw new Error("Public URL Check returned an invalid address");m.some(S=>S.address===x)||m.push({address:x,family:(0,Dn.isIP)(x)})}c&&m.sort((g,x)=>g.family-x.family);let h=m[0];if(!h)throw new Error("Public URL Check hostname has no addresses");return{origin:r,...h,addresses:m}}a(dd,"preparePublicUrlCheck");function cm(e,t,n){let r=e.hostname.replace(/^\[|\]$/g,"");return new Promise((o,s)=>{let d=(e.protocol==="https:"?ad.default:id.default).request(e,{method:"GET",agent:!1,signal:n,headers:{Host:e.host},...(0,Dn.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(cm,"requestPinned");async function ld(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 cm(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(ld,"requestPublicUrlCheck");ye();function um(e){return hn(e.settings.httpAdminRoot)}a(um,"mcpPageUrl");function pm(e){return typeof e=="boolean"||typeof e=="number"||typeof e=="string"?e:Array.isArray(e)?e.join(", "):typeof e=="function"?"custom function":!1}a(pm,"normalizeTrustProxy");function pd(e){if(!e)return"Set a Public URL before enabling OAuth.";let t=new URL(e),n=at(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(pd,"oauthPublicUrlWarning");function fd(e,t,n){let{sources:r,...o}=t.getEffective(),{permission:s}=Kn(e,n);return{ok:!0,adminAuthEnabled:W(e.settings),canEdit:s==="*",permission:s,mcpPageUrl:um(e),apiBase:de(e.settings.httpAdminRoot),settings:o,oauthPublicUrlWarning:pd(o.publicBaseUrl),sources:r,disk:t.getDisk(),trustProxy:pm(n.app.get("trust proxy")),precedence:["settings.js","disk","default"]}}a(fd,"settingsResponse");function md(e,t){return(n,r)=>{r.json(fd(e,t,n))}}a(md,"getSettingsHandler");function cd(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(cd,"parseBooleanField");function ud(e,t,n){let r=e[t];if(r!==void 0){if(typeof r!="number"||!Number.isFinite(r)||r<=0)throw new Error(`${t} must be a positive number`);n[t]=Math.floor(r)}}a(ud,"parsePositiveIntField");function fm(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(fm,"parseRateLimitField");function mm(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=>pn(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(mm,"parseCorsField");function hd(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[l,c]of Object.entries(t.getEffective().sources))c==="settings.js"&&delete i[l];t.getEffective().sources.oauth==="settings.js"&&delete i.oauth;let d={};try{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 l=i.oauth,c={};if(l.enabled!==void 0){if(typeof l.enabled!="boolean")throw new Error("oauth.enabled must be a boolean");c.enabled=l.enabled}for(let p of["accessTokenTtlSec","refreshIdleTtlSec","dcrMaxClients"])if(l[p]!==void 0){if(typeof l[p]!="number"||!Number.isInteger(l[p])||l[p]<1||l[p]>31536e3)throw new Error(`oauth.${p} must be a positive integer`);c[p]=l[p]}let u=l.refreshMaxLifetimeSec;if(u!==void 0){if(typeof u!="number"||!Number.isInteger(u)||u<0||u>31536e4)throw new Error("oauth.refreshMaxLifetimeSec must be a non-negative integer");c.refreshMaxLifetimeSec=u}d.oauth=c}cd(i,"enabled",d),cd(i,"allowWithoutAdminAuth",d),ud(i,"defaultLinkTtlSeconds",d),ud(i,"maxActiveLinks",d)}catch(l){o.status(400).json({error:l.message,code:"invalid_body"});return}if(i.defaultDeploymentType!==void 0){let l=i.defaultDeploymentType;if(l!=="nodes"&&l!=="flows"&&l!=="full"){o.status(400).json({error:"defaultDeploymentType must be 'nodes', 'flows', or 'full'",code:"invalid_body"});return}d.defaultDeploymentType=l}try{i.publicBaseUrl!==void 0&&(d.publicBaseUrl=Ct(i.publicBaseUrl)),mm(i,d);for(let l of["rateLimitWindowMs","rateLimitMaxPerIp","rateLimitMaxPerToken"])fm(i,l,d)}catch(l){o.status(400).json({error:l.message,code:"invalid_body"});return}try{let l=t.patch(d);Vn(l.publicBaseUrl),n.configureDefaults({defaultTtlSeconds:l.defaultLinkTtlSeconds,maxActiveLinks:l.maxActiveLinks});let c=fd(e,t,r),u=l.publicBaseUrl?new URL(l.publicBaseUrl):void 0,p=u?at(u.hostname.replace(/^\[|\]$/g,"")):void 0,f=p?jn(p):void 0,m=[];f!==void 0&&f!=="public"&&m.push("Public URL uses a private or non-public IP address.");let h=pd(l.publicBaseUrl);h&&l.publicBaseUrl&&m.push(h),o.json({...c,...m.length?{warnings:m}:{}})}catch(l){if(l instanceof X){o.status(l.statusCode).json({error:l.message,code:l.code});return}$(o,l);return}}}a(hd,"putSettingsHandler");function gd(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"||jn(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 dd(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+de(e.settings.httpAdminRoot);let d=await ld(i,de(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(gd,"checkPublicUrlHandler");ye();it();ye();K();Gn();var hm="com55-ai-access";function wd(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(wd,"acceptedMediaTypes");function bd(e){let t=wd(e.headers.accept);return t.has("text/event-stream")&&!t.has("text/html")}a(bd,"isMcpStreamRequest");function gm(e,t,n){e.httpAdmin.get("/mcp/api/v1",t,n,Mo(e)),e.httpAdmin.get("/mcp/api/v1/",t,n,Mo(e)),e.httpAdmin.get("/mcp/api/v1/openapi.yaml",t,n,Ma()),e.httpAdmin.get("/mcp/api/v1/openapi.json",t,n,Da()),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",Xt),o.type("application/javascript").status(200).send(yr())})}a(gm,"registerMcpShellAndDiscovery");var ym=/^\/mcp\/(?:api\/v1\/proposals|rpc$|a\/[^/]+\/(?:proposals|propose|draft))/;function vd(e,t={}){let n=Vr(e,{...t,linkStore:t.linkStore??Ur(e)}),r=Ei(()=>n.settingsStore.getEffective()),{ip:o,token:s}=r,i=new Br(Bn(e.settings.userDir),()=>n.settingsStore.getEffective().oauth.dcrMaxClients),d=a(()=>n.settingsStore.getEffective().publicBaseUrl,"publicBase"),l=a(()=>`${d().replace(/\/$/,"")}/mcp`,"mcpUrl"),c=new qr(i,l,()=>n.settingsStore.getEffective().oauth.accessTokenTtlSec,()=>n.settingsStore.getEffective().oauth.refreshIdleTtlSec,()=>n.settingsStore.getEffective().oauth.refreshMaxLifetimeSec),u=Na(e,c,d,l);hs(e.httpAdmin,ms(()=>n.settingsStore.getEffective().corsAllowedOrigins));let p=a(w=>(Array.isArray(w)?w:w?[w]:[]).flatMap(b=>{if(typeof b=="string")return[b];let k=b;return k&&typeof k.path=="string"?[{path:k.path,root:typeof k.root=="string"?k.root:void 0}]:[]}),"staticMounts"),f={authorization:[],metadata:[],details:[],httpNodeRootFallback:e.settings.httpNodeRoot===!1||!e.httpNode},m="",h=a(()=>{let w=Ia({httpAdmin:e.httpAdmin,httpNode:e.httpNode,httpAdminRoot:e.settings.httpAdminRoot,httpNodeRoot:e.settings.httpNodeRoot,metadataFallback:f.httpNodeRootFallback,eachNode:e.nodes?M=>e.nodes?.eachNode(M):void 0,staticDirs:p(e.settings.httpStatic),httpStaticRoot:e.settings.httpStaticRoot,fileExists:yd.existsSync}),b=w.authorization,k=w.metadata;f.authorization=b.map(os),f.metadata=k.map(os),f.details=[...b.map(M=>({...M,kind:"authorization"})),...k.map(M=>({...M,kind:"metadata"}))];let L=[...f.authorization.map(M=>`${M} \u2014 OAuth authorization routes disabled`),...f.metadata.map(M=>`${M} \u2014 OAuth metadata disabled`)],H=L.join(`
6373
- `);if(H!==m)for(let M of L)e.log?.warn?.(`AI Access OAuth route collision: ${M}`);m=H},"refreshCollisions");h(),e.events?.on("flows:started",h);let g=Ho({windowMs:6e4,maxPerIp:30}),x=a((w,b,k)=>{if(!n.settingsStore.getEffective().oauth.enabled||f.authorization.length||!w.path.startsWith("/mcp/oauth/")){k();return}let L=w.path.replace(/\/+$/,"").toLowerCase(),H=a(()=>Et(e)(w,b,()=>u.server(w,b,k)),"run");L==="/mcp/oauth/authorize"?o(w,b,()=>g(w,b,H)):L.startsWith("/mcp/oauth/consent/")?o(w,b,H):H()},"oauthServerMiddleware");x.oauthOwned=!0,e.httpAdmin.use(x);let S=a((w,b,k)=>{if(!n.settingsStore.getEffective().oauth.enabled||f.metadata.length){k();return}u.metadata(w,b,k)},"metadataHandler");S.oauthOwned=!0,f.httpNodeRootFallback?e.httpAdmin.use("/mcp",S):e.httpNode?.use(S),e.httpAdmin.get("/mcp/api/v1/oauth/clients",o,s,we(e,"api"),(w,b)=>b.json({clients:i.data.clients.map(k=>({client_id:k.client_id,client_name:k.client_name,redirect_uris:k.redirect_uris,lastUsedAt:k.lastUsedAt}))})),e.httpAdmin.get("/mcp/api/v1/oauth/status",o,s,we(e,"api"),(w,b)=>{let k=!W(e.settings)||We(w)==="*";b.json({...f,details:k?f.details:f.details.map(({file:L,...H})=>H),collisions:[...f.authorization,...f.metadata],oauthEnabled:n.settingsStore.getEffective().oauth.enabled})}),e.httpAdmin.delete("/mcp/api/v1/oauth/clients/:id",o,s,we(e,"api"),gn(e),(w,b)=>b.json({revoked:i.revokeClient(String(w.params.id))})),e.httpAdmin.use((w,b,k)=>{if(!ym.test(w.path)){k();return}n.flowAdapter.getFlows().then(L=>Vs(w,dr(L.flows))).catch(()=>{}).finally(()=>k())}),na(e,{linkStore:n.linkStore,proposalStore:n.proposalStore,auditLog:n.auditLog,flowAdapter:n.flowAdapter,nodeTypesAdapter:n.nodeTypesAdapter,...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{},rateLimiters:r}),e.httpAdmin.get("/ai-access",o,s,(w,b)=>{b.setHeader("Cache-Control","no-store, no-cache, must-revalidate, max-age=0"),b.setHeader("Pragma","no-cache"),b.setHeader("Expires","0"),b.type("html").status(200).send(Ba({apiBase:de(e.settings.httpAdminRoot),mcpRpc:qe(e.settings.httpAdminRoot),editorRoot:ae(e.settings.httpAdminRoot),authTokenKey:Yn(e.settings.httpAdminRoot),dashboardPath:hn(e.settings.httpAdminRoot)}))}),e.httpAdmin.get("/mcp",o,(w,b,k)=>{if(bd(w)){k();return}b.setHeader("Cache-Control","no-store"),b.redirect(302,hn(e.settings.httpAdminRoot))}),gm(e,o,s),e.httpAdmin.get("/mcp/api/v1/status",o,s,vs(e,w=>n.linkStore.validateResult(w)),R((w,b)=>{b.json(rn(e,w))})),e.httpAdmin.get("/mcp/api/v1/settings",o,s,we(e,"api"),R(md(e,n.settingsStore))),e.httpAdmin.put("/mcp/api/v1/settings",o,s,we(e,"api"),gn(e),je("rest"),R(hd(e,n.settingsStore,n.linkStore))),e.httpAdmin.post("/mcp/api/v1/settings/check-public-url",o,s,we(e,"api"),gn(e),je("rest"),R(gd(e)));let E=[o,s,we(e,"api"),Et(e),gn(e)];e.httpAdmin.get("/mcp/api/v1/links",...E,R(ua(e,n.linkStore))),e.httpAdmin.post("/mcp/api/v1/links",...E,R(pa(e,n.linkStore,n.auditLog))),e.httpAdmin.delete("/mcp/api/v1/links/:id",...E,R(fa(e,n.linkStore,n.auditLog)));let I=[o,s,Et(e),bs(e,w=>n.linkStore.validateResult(w))],N=n.flowAdapter,me={nodeTypes:n.nodeTypesAdapter,...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{}};e.httpAdmin.get("/mcp/api/v1/flows",...I,R(vi(N))),e.httpAdmin.get("/mcp/api/v1/flows/:id",...I,R(Lr(N))),e.httpAdmin.get("/mcp/api/v1/nodes/:id",...I,R(Cr(N))),e.httpAdmin.get("/mcp/api/v1/nodes/:id/field/:name",...I,R(Er(N))),e.httpAdmin.get("/mcp/api/v1/search",...I,R(Ir(N))),e.httpAdmin.get("/mcp/api/v1/node-types",...I,R(Nr(me))),e.httpAdmin.get("/mcp/api/v1/node-types/:type",...I,R(Fr(me))),e.httpAdmin.get("/mcp/api/v1/palette/search",...I,R(Or(me)));let A=e.settings.httpAdminRoot;e.httpAdmin.get("/mcp/api/v1/proposals",...I,R(za(n.proposalStore,n.flowAdapter,A))),e.httpAdmin.get("/mcp/api/v1/proposals/:id",...I,R(Ga(n.proposalStore,A,n.flowAdapter))),e.httpAdmin.get("/mcp/api/v1/proposals/:id/diff",...I,R(Xa(n.proposalStore,n.flowAdapter))),e.httpAdmin.post("/mcp/api/v1/proposals",o,s,je("rest"),Nt(e,n.linkStore,"rest"),R(Wa(n.proposalStore,n.auditLog,n.flowAdapter,A,n.nodeTypesAdapter))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/approve",...E,je("rest"),R(Ja(n.proposalService,A))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/reject",...E,je("rest"),R(Ka(n.proposalService,A))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/withdraw",o,s,je("rest"),Nt(e,n.linkStore,"rest"),R(Va(n.proposalStore,n.auditLog,A)));let v=[o,s,(w,b,k)=>{let L=w.headers.authorization;if(!n.settingsStore.getEffective().oauth.enabled||L?.toLowerCase().startsWith("bearer aia_")){if(n.settingsStore.getEffective().oauth.enabled){let H=b.setHeader.bind(b);b.setHeader=((M,Re)=>{if(M.toLowerCase()==="www-authenticate"&&typeof Re=="string"&&!Re.includes("resource_metadata=")){let Ue=`${d().replace(/\/$/,"")}${f.httpNodeRootFallback?"/mcp":""}/.well-known/oauth-protected-resource/mcp`;Re+=`, resource_metadata="${Ue}", scope="read propose"`}return H(M,Re)})}Nt(e,n.linkStore,"mcp")(w,b,k);return}Et(e)(w,b,()=>u.auth(w,b,k))}];e.httpAdmin.post("/mcp/rpc",...v,je("mcp"),R(Qo(e,n))),e.httpAdmin.get("/mcp/rpc",...v,On),e.httpAdmin.delete("/mcp/rpc",...v,On),e.httpAdmin.post("/mcp",...v,je("mcp"),R(Qo(e,n))),e.httpAdmin.get("/mcp",...v,On),e.httpAdmin.delete("/mcp",...v,On),e.httpAdmin.get("/mcp/api/v1/logs",o,s,we(e,"api"),Et(e),R(ma(n.auditLog,n.linkStore))),e.httpAdmin.use("/mcp/api/v1",yi()),ia(e)}a(vd,"registerRoutes");function wm(e){e.plugins.registerPlugin(hm,{type:"com55-ai-access",onadd(){vd(e),e.log?.info?.("Node-RED AI Access plugin loaded (dashboard /ai-access, MCP /mcp)")}})}a(wm,"registerAiAccessPlugin");var bm=wm;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});
6544
+ </html>`}a(Bd,"renderMcpPage");Re();xe();Ze();G();pt();J();De();tt();Re();nt();function Nt(e,t,n,r={}){e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("Content-Type","application/json; charset=utf-8");let o={error:je[n]??"Request failed",code:n,...r},s=de(n,"rest");s&&typeof o.hint!="string"&&(o.hint=s),e.status(t).json(o)}a(Nt,"sendSafeJsonError");function qd(e){let t={kind:"admin"};return typeof e.user?.username=="string"&&(t.username=e.user.username),t}a(qd,"adminActor");function zd(e,t,n,r,o){return async(s,i)=>{if(!s.accessLink){i.setHeader("WWW-Authenticate",X),i.status(401).json({error:me,code:Ie});return}try{let d=q(s,r),l=ie(s.query),c=await rt(e,t,s.accessLink,s.body??{},n,d,o),u=V(e,c.expectedRev,{flowId:c.payload.flowId}),p=Oe(e,e.get(c.id),s.accessLink.id),f=c.warnings;i.status(201).json({...he(c,{...d,include:l}),mine:!0,...$e(p),...Z(u),...f&&f.length>0?{warnings:f}:{}})}catch(d){let l=M(d,"rest");if(l){i.status(l.statusCode).json(l.body);return}D(i,d);return}}}a(zd,"createProposalHandler");function Wd(e,t,n){return async(r,o)=>{let s,i;try{let T=await t.getFlows();s=T.rev,i=Rr(T.flows)}catch{}let d={...q(r,n),...i?{liveFlowLabels:i}:{}},l=ie(r.query),c;try{c=Mr(r.query)}catch(T){D(o,T);return}let u=s?V(e,s):0,p=r.accessLink;if(p){let T=Zt(e,p,d,c),C=T.proposals.map(L=>{let N=ge(e,p,L,{...d,include:l},{includeConflicts:!1});return N.status!=="pending"||s===void 0?N:{...N,baseRevStale:N.expectedRev!==s}});o.json({proposals:C,...kt({...T,returned:T.proposals.length}),...Z(u)});return}let f={};c.flowId&&(f.flowId=c.flowId);let m=Ti(e.list(f).filter(T=>ns(T.status,c.status)),{...d,include:l}),h=c.offset??0,k=(c.limit!==void 0?m.slice(h,h+c.limit):h>0?m.slice(h):m).map(T=>T.status!=="pending"||s===void 0?T:{...T,baseRevStale:T.expectedRev!==s});o.json({proposals:k,...kt({total:m.length,limit:c.limit??null,offset:h,returned:k.length}),...Z(u)})}}a(Wd,"listProposalsHandler");function Gd(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=q(r,t),d=ie(r.query),l;if(n)try{l=(await n.getFlows()).rev}catch{}try{if(r.accessLink){let c=ze(e,r.accessLink,s,i);o.json(ge(e,r.accessLink,c,{...i,include:d},{...l?{liveRev:l}:{}}));return}l&&V(e,l),o.json(he(e.get(s),{...i,include:d}))}catch(c){if(c instanceof se){Nt(o,404,c.code);return}let u=M(c,"rest");if(u){o.status(u.statusCode).json(u.body);return}D(o,c);return}}}a(Gd,"getProposalHandler");function Kd(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.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,qd(n),i);r.json(ae(d,q(n,t)))}catch(d){if(d instanceof se||d instanceof ne||d instanceof In||d instanceof Y){Nt(r,d.statusCode,d.code);return}D(r,d);return}}}a(Kd,"approveProposalHandler");function Jd(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=ae(e.reject(o,qd(n),i),q(n,t));r.json(d)}catch(d){if(d instanceof se||d instanceof ne||d instanceof Y){Nt(r,d.statusCode,d.code);return}D(r,d);return}}}a(Jd,"rejectProposalHandler");function Vd(e,t,n){return(r,o)=>{if(!r.accessLink){o.setHeader("WWW-Authenticate",X),o.status(401).json({error:me,code:Ie});return}let s=r.params.id;if(typeof s!="string"||s.length===0){o.status(400).json({error:"Missing proposal id",code:"missing_proposal_id"});return}let i=r.body??{},d=q(r,n),l=ie(r.query);try{let c=Qt(e,t,r.accessLink,s,i.reason,d);o.json(ge(e,r.accessLink,c,{...d,include:l}))}catch(c){let u=M(c,"rest");if(u){Nt(o,u.statusCode,u.body.code);return}Nt(o,500,"internal_error");return}}}a(Vd,"withdrawProposalHandler");function Yd(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?ze(e,n.accessLink,o):e.get(o),i=sn(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=an(await un(s,Me(n,t)),i.format);r.json(d)}catch(s){if(s instanceof se){Nt(r,404,s.code);return}if(s instanceof W||s instanceof Se||s instanceof Ne){Nt(r,s.statusCode,s.code);return}let i=M(s,"rest");if(i){r.status(i.statusCode).json(i.body);return}D(r,s);return}}}a(Yd,"getProposalDiffHandler");nt();xe();xe();Ar();function Rh(e){return e.endsWith("/")?e.slice(0,-1):e}a(Rh,"trimTrailingSlash");var mo=class{static{a(this,"HttpAdapter")}kind="http";baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=Rh(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 W(n):t.status===409?new K("unknown","unknown",`Admin API ${n}`):t.status===401||t.status===403?new Ne(hr):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 Nn(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 W(t);let s=await this.readJson(r,"GET /flows"),i=await this.readJson(o,`GET /flow/${t}`);return{rev:Nn(s,"GET /flows").rev,flow:qt(t,i)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new K(t.expectedRev,r.rev,"updateFlow");let o=kr(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 K(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}}};xe();Ar();function _h(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(_h,"requireFlowsApi");function Ph(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(Ph,"resolveRev");var ho=class{static{a(this,"RedAdapter")}kind="red";flows;constructor(t){this.flows=_h(t)}async getFlows(){let t=await this.flows.getFlows({});return Nn(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 Eh(o)?new W(t):o}return{rev:n.rev,flow:qt(t,r)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new K(t.expectedRev,r.rev,"updateFlow");let o=kr(r.flows,t.flowId,t.flow),s=await this.flows.setFlows({flows:{flows:o,rev:t.expectedRev},deploymentType:n});return{flowId:t.flowId,rev:Ph(s)}}};function Xd(e){let t=e?.runtime?.flows;return!!(t?.getFlows&&t.getFlow&&t.setFlows)}a(Xd,"canUseRedRuntime");function Eh(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(Eh,"isRuntimeFlowNotFound");var Zd=require("module");function Qd(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(Qd,"isFlowsApi");function Ch(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Zd.createRequire)(e)}a(Ch,"createPluginRequire");function Lh(e){try{let t=e("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}a(Lh,"isNodeRedRuntimeInitialized");function Ih(){try{let e=Ch();if(!Lh(e))return null;let t=e("@node-red/runtime");return Qd(t.flows)?t.flows:null}catch{return null}}a(Ih,"tryRequireInitializedRuntimeFlows");function el(e){if(e?.runtime&&Qd(e.runtime.flows))return e;let t=Ih();return t?{runtime:{flows:t}}:null}a(el,"resolveRuntimeHost");function tl(e={}){let t=el(e.RED??null);if(t&&Xd(t))return new ho(t);let n=e.adminAuthEnabled===!0;if(e.http?.baseUrl){if(n&&!e.http.accessToken)throw new Se("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 mo(e.http)}throw new Se("No usable Node-RED flow adapter: in-process flows API is unavailable and no HTTP Admin API baseUrl was provided")}a(tl,"createFlowAdapter");xe();var yo=require("fs"),nl=require("path");yr();G();var Nh=/^[a-z][a-z0-9._-]{0,63}$/;function Fh(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(Fh,"sanitizeActor");var go=class{constructor(t,n={}){this.paths=t;this.filePath=(0,nl.join)(t.logs,"audit.jsonl"),this.now=n.now??(()=>Date.now()),this.maxAppendBytes=n.maxAppendBytes??Ms}static{a(this,"AuditLog")}filePath;now;maxAppendBytes;seq=0;append(t,n,r){let o=Nh.test(t)?t:"unknown";this.seq+=1;let s={id:`log-${this.now()}-${this.seq}`,at:new Date(this.now()).toISOString(),action:o,actor:Fh(n)};r!==void 0&&(s.details=ni(r));let i=Je(s,this.maxAppendBytes,{kind:"audit"});return js(this.paths.root,this.filePath,i),s}list(t=100){if(!(0,yo.existsSync)(this.filePath))return[];let n=(0,yo.readFileSync)(this.filePath,"utf8").split(`
6545
+ `).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()}};Ze();dr();ce();wt();xe();Ze();tt();Re();var Oh="Flow revision conflict: the expectedRev is stale. Ask the AI to re-read flows and resubmit a new proposal.";function rl(e){let t=ae(e),n=Pr(e)||void 0;return{proposalId:e.id,title:t.title,displayName:t.displayName,flowId:e.payload.flowId,...n?{flowLabel:n}:{}}}a(rl,"auditProposalMeta");var wo=class{constructor(t,n,r,o){this.proposals=t;this.audit=n;this.adapter=r;this.getDeploymentType=o}static{a(this,"ProposalService")}async approve(t,n,r={}){let o=this.proposals.beginApprove(t),s=rl(o),i=r.deploymentType??this.getDeploymentType();try{let d=o.type;if(d!=="replace_flow"){let l=this.proposals.markFailed(t,n,`Unsupported proposal type '${d}'`);return this.audit.append("proposal.failed",n,{...s,reason:l.reason}),l}try{let l=await this.adapter.updateFlow({flowId:o.payload.flowId,flow:o.payload.flow,expectedRev:o.expectedRev,deploymentType:i}),c=this.proposals.markApproved(t,n,l.rev);return this.audit.append("proposal.approved",n,{...s,resultRev:l.rev,flowId:o.payload.flowId}),Pi(this.proposals,o.payload.flowId,t,l.rev),c}catch(l){if(l instanceof K){let p=this.proposals.markRejected(t,n,Oh);return this.audit.append("proposal.rejected",n,{...s,reason:p.reason,code:l.code,expectedRev:l.expectedRev,currentRev:l.currentRev}),p}let c=l instanceof Error?l.message:String(l),u=this.proposals.markFailed(t,n,c);return this.audit.append("proposal.failed",n,{...s,reason:c}),u}}finally{this.proposals.releaseLock(t)}}reject(t,n,r){let o=this.proposals.get(t);if(o.status!=="pending")throw new ne(t,o.status);let s=rl(o),i=this.proposals.markRejected(t,n,r.trim()||"Rejected by admin");return this.audit.append("proposal.rejected",n,{...s,reason:i.reason}),i}};ue();function $h(e){let t=a(()=>Promise.reject(new Ne(e)),"fail");return{kind:"http",getFlows:t,getFlow:t,updateFlow:t}}a($h,"misconfiguredAdapter");function ol(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(ol,"localAdminBaseUrl");function bo(e,t={}){let n=nr(e.settings.userDir);Hs(n);let r=t.settingsStore??new Mt(n,e.settings.aiAccess,u=>e.log?.warn?.(u));t.bindSettings!==!1&&cr(r),pr(r.getEffective().publicBaseUrl);let o=t.linkStore??uo(e);o.configureDefaults({defaultTtlSeconds:r.getEffective().defaultLinkTtlSeconds,maxActiveLinks:r.getEffective().maxActiveLinks});let s=new wr(n),i=new go(n),d;if(t.flowAdapter)d=t.flowAdapter;else try{d=tl({RED:e,http:{baseUrl:ol(e)},adminAuthEnabled:z(e.settings)})}catch(u){let p=u instanceof Se?u.message:hr;e.log?.warn?.(`AI Access flow adapter unavailable: ${p}`),d=$h(p)}let l;if(t.nodeTypesAdapter)l=t.nodeTypesAdapter;else if(t.flowAdapter)l=$o();else try{l=mi({baseUrl:ol(e)})}catch(u){let p=u instanceof Error?u.message:String(u);e.log?.warn?.(`AI Access node-types adapter unavailable: ${p}`),l=$o([])}let c=new wo(s,i,d,()=>ve(e.settings).defaultDeploymentType);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(bo,"createPluginServices");G();ce();Le();rr();var sl=yn(require("dns"),1),il=yn(require("http"),1),al=yn(require("https"),1),Zn=require("net");ce();function Mh(e,t){return e===10||e===172&&t>=16&&t<=31||e===192&&t===168}a(Mh,"isPrivateV4");function Dh(e,t){return e===169&&t===254}a(Dh,"isLinkLocalV4");function jh(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(jh,"isSpecialUseV4");function Hh(e){let[t=0,n=0,r=0]=e.split(".").map(Number);return t===127?"loopback":Mh(t,n)||Dh(t,n)||jh(t,n,r)||e==="168.63.129.16"?"blocked":"public"}a(Hh,"classifyIpv4");function Uh(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(Uh,"expandIpv6");function Bh(e){let t=Uh(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(Bh,"classifyIpv6");function Qn(e){let t=ft(e);return t?(0,Zn.isIP)(t)===4?Hh(t):Bh(t):"blocked"}a(Qn,"classifyPublicUrlAddress");function qh(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(qh,"isMetadataHostname");async function dl(e,t,n){let r=Dt(e);if(!r)throw new Error("publicBaseUrl is required for Check");let o=new URL(r),s=o.hostname.replace(/^\[|\]$/g,"");if(qh(s))throw new Error("Public URL Check cannot target metadata hosts");let i=ft(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&&Qn(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 k=Qn(g);if(k==="blocked"||k==="loopback"&&!c||c&&k!=="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,Zn.isIP)(i)};let f=await new Promise((g,k)=>{let T=a(()=>k(n.reason instanceof Error?n.reason:new Error(String(n.reason))),"abort");n.throwIfAborted(),n.addEventListener("abort",T,{once:!0}),sl.default.lookup(s,{all:!0},(C,L)=>{n.removeEventListener("abort",T),C?k(C):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 k=ft(g.address);if(!k)throw new Error("Public URL Check returned an invalid address");m.some(T=>T.address===k)||m.push({address:k,family:(0,Zn.isIP)(k)})}c&&m.sort((g,k)=>g.family-k.family);let h=m[0];if(!h)throw new Error("Public URL Check hostname has no addresses");return{origin:r,...h,addresses:m}}a(dl,"preparePublicUrlCheck");function zh(e,t,n){let r=e.hostname.replace(/^\[|\]$/g,"");return new Promise((o,s)=>{let d=(e.protocol==="https:"?al.default:il.default).request(e,{method:"GET",agent:!1,signal:n,headers:{Host:e.host},...(0,Zn.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(zh,"requestPinned");async function ll(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 zh(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(ll,"requestPublicUrlCheck");Sn();ue();function Wh(e){return Rn(e.settings.httpAdminRoot)}a(Wh,"mcpPageUrl");function Gh(e){return typeof e=="boolean"||typeof e=="number"||typeof e=="string"?e:Array.isArray(e)?e.join(", "):typeof e=="function"?"custom function":!1}a(Gh,"normalizeTrustProxy");function pl(e){if(!e)return"Set a Public URL before enabling OAuth.";let t=new URL(e),n=ft(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(pl,"oauthPublicUrlWarning");function fl(e,t,n){let{sources:r,...o}=t.getEffective(),{permission:s}=ur(e,n);return{ok:!0,adminAuthEnabled:z(e.settings),canEdit:s==="*",permission:s,mcpPageUrl:Wh(e),apiBase:fe(e.settings.httpAdminRoot),settings:o,oauthPublicUrlWarning:pl(o.publicBaseUrl),sources:r,disk:t.getDisk(),trustProxy:Gh(n.app.get("trust proxy")),precedence:["settings.js","disk","default"],defaults:{secretNamePatterns:[...ct]}}}a(fl,"settingsResponse");function ml(e,t){return(n,r)=>{r.json(fl(e,t,n))}}a(ml,"getSettingsHandler");function cl(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(cl,"parseBooleanField");function ul(e,t,n){let r=e[t];if(r!==void 0){if(typeof r!="number"||!Number.isFinite(r)||r<=0)throw new Error(`${t} must be a positive number`);n[t]=Math.floor(r)}}a(ul,"parsePositiveIntField");function Kh(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(Kh,"parseRateLimitField");function Jh(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=>vn(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(Jh,"parseCorsField");function Vh(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>kn)throw new Error(`secretNamePatterns allows at most ${kn} entries`);for(let o of r)if(!or.test(o.trim()))throw new Error('secretNamePatterns entries must be 2-40 letters, digits, "_" or "-" (e.g. API_KEY)');t.secretNamePatterns=An(r)??[]}}a(Vh,"parseSecretPatternsField");function hl(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.oauth!==void 0){if(!i.oauth||typeof i.oauth!="object"||Array.isArray(i.oauth))throw new Error("oauth must be an object");let c=i.oauth,u={};if(c.enabled!==void 0){if(typeof c.enabled!="boolean")throw new Error("oauth.enabled must be a boolean");u.enabled=c.enabled}for(let f of["accessTokenTtlSec","refreshIdleTtlSec","dcrMaxClients"])if(c[f]!==void 0){if(typeof c[f]!="number"||!Number.isInteger(c[f])||c[f]<1||c[f]>31536e3)throw new Error(`oauth.${f} must be a positive integer`);u[f]=c[f]}let p=c.refreshMaxLifetimeSec;if(p!==void 0){if(typeof p!="number"||!Number.isInteger(p)||p<0||p>31536e4)throw new Error("oauth.refreshMaxLifetimeSec must be a non-negative integer");u.refreshMaxLifetimeSec=p}d.oauth=u}cl(i,"enabled",d),cl(i,"allowWithoutAdminAuth",d),ul(i,"defaultLinkTtlSeconds",d),ul(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=Dt(i.publicBaseUrl)),Jh(i,d),Vh(i,d,l);for(let c of["rateLimitWindowMs","rateLimitMaxPerIp","rateLimitMaxPerToken"])Kh(i,c,d)}catch(c){o.status(400).json({error:c.message,code:"invalid_body"});return}try{let c=t.patch(d,l);pr(c.publicBaseUrl),n.configureDefaults({defaultTtlSeconds:c.defaultLinkTtlSeconds,maxActiveLinks:c.maxActiveLinks});let u=fl(e,t,r),p=c.publicBaseUrl?new URL(c.publicBaseUrl):void 0,f=p?ft(p.hostname.replace(/^\[|\]$/g,"")):void 0,m=f?Qn(f):void 0,h=[];m!==void 0&&m!=="public"&&h.push("Public URL uses a private or non-public IP address.");let g=pl(c.publicBaseUrl);g&&c.publicBaseUrl&&h.push(g),o.json({...u,...h.length?{warnings:h}:{}})}catch(c){if(c instanceof Y){o.status(c.statusCode).json({error:c.message,code:c.code});return}D(o,c);return}}}a(hl,"putSettingsHandler");function gl(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"||Qn(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 dl(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+fe(e.settings.httpAdminRoot);let d=await ll(i,fe(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(gl,"checkPublicUrlHandler");ue();pt();ue();J();dr();var Yh="com55-ai-access";function wl(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(wl,"acceptedMediaTypes");function bl(e){let t=wl(e.headers.accept);return t.has("text/event-stream")&&!t.has("text/html")}a(bl,"isMcpStreamRequest");function Xh(e,t,n){e.httpAdmin.get("/mcp/api/v1",t,n,ls(e)),e.httpAdmin.get("/mcp/api/v1/",t,n,ls(e)),e.httpAdmin.get("/mcp/api/v1/openapi.yaml",t,n,Md()),e.httpAdmin.get("/mcp/api/v1/openapi.json",t,n,Dd()),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",on),o.type("application/javascript").status(200).send(Hr())})}a(Xh,"registerMcpShellAndDiscovery");var Zh=/^\/mcp\/(?:api\/v1\/proposals|rpc$|a\/[^/]+\/(?:proposals|propose|draft))/;function vl(e,t={}){let n=bo(e,{...t,linkStore:t.linkStore??uo(e)}),r=xa(()=>n.settingsStore.getEffective()),{ip:o,token:s}=r,i=new po(nr(e.settings.userDir),()=>n.settingsStore.getEffective().oauth.dcrMaxClients),d=a(()=>n.settingsStore.getEffective().publicBaseUrl,"publicBase"),l=a(()=>`${d().replace(/\/$/,"")}/mcp`,"mcpUrl"),c=new fo(i,l,()=>n.settingsStore.getEffective().oauth.accessTokenTtlSec,()=>n.settingsStore.getEffective().oauth.refreshIdleTtlSec,()=>n.settingsStore.getEffective().oauth.refreshMaxLifetimeSec),u=Nd(e,c,d,l);Bs(e.httpAdmin,Us(()=>n.settingsStore.getEffective().corsAllowedOrigins));let p=a(w=>(Array.isArray(w)?w:w?[w]:[]).flatMap(v=>{if(typeof v=="string")return[v];let A=v;return A&&typeof A.path=="string"?[{path:A.path,root:typeof A.root=="string"?A.root:void 0}]:[]}),"staticMounts"),f={authorization:[],metadata:[],details:[],httpNodeRootFallback:e.settings.httpNodeRoot===!1||!e.httpNode},m="",h=a(()=>{let w=Id({httpAdmin:e.httpAdmin,httpNode:e.httpNode,httpAdminRoot:e.settings.httpAdminRoot,httpNodeRoot:e.settings.httpNodeRoot,metadataFallback:f.httpNodeRootFallback,eachNode:e.nodes?F=>e.nodes?.eachNode(F):void 0,staticDirs:p(e.settings.httpStatic),httpStaticRoot:e.settings.httpStaticRoot,fileExists:yl.existsSync}),v=w.authorization,A=w.metadata;f.authorization=v.map(Ls),f.metadata=A.map(Ls),f.details=[...v.map(F=>({...F,kind:"authorization"})),...A.map(F=>({...F,kind:"metadata"}))];let E=[...f.authorization.map(F=>`${F} \u2014 OAuth authorization routes disabled`),...f.metadata.map(F=>`${F} \u2014 OAuth metadata disabled`)],$=E.join(`
6546
+ `);if($!==m)for(let F of E)e.log?.warn?.(`AI Access OAuth route collision: ${F}`);m=$},"refreshCollisions");h(),e.events?.on("flows:started",h);let g=ps({windowMs:6e4,maxPerIp:30}),k=a((w,v,A)=>{if(!n.settingsStore.getEffective().oauth.enabled||f.authorization.length||!w.path.startsWith("/mcp/oauth/")){A();return}let E=w.path.replace(/\/+$/,"").toLowerCase(),$=a(()=>jt(e)(w,v,()=>u.server(w,v,A)),"run");E==="/mcp/oauth/authorize"?o(w,v,()=>g(w,v,$)):E.startsWith("/mcp/oauth/consent/")?o(w,v,$):$()},"oauthServerMiddleware");k.oauthOwned=!0,e.httpAdmin.use(k);let T=a((w,v,A)=>{if(!n.settingsStore.getEffective().oauth.enabled||f.metadata.length){A();return}u.metadata(w,v,A)},"metadataHandler");T.oauthOwned=!0,f.httpNodeRootFallback?e.httpAdmin.use("/mcp",T):e.httpNode?.use(T),e.httpAdmin.get("/mcp/api/v1/oauth/clients",o,s,Ae(e,"api"),(w,v)=>v.json({clients:i.data.clients.map(A=>({client_id:A.client_id,client_name:A.client_name,redirect_uris:A.redirect_uris,lastUsedAt:A.lastUsedAt}))})),e.httpAdmin.get("/mcp/api/v1/oauth/status",o,s,Ae(e,"api"),(w,v)=>{let A=!z(e.settings)||Ye(w)==="*";v.json({...f,details:A?f.details:f.details.map(({file:E,...$})=>$),collisions:[...f.authorization,...f.metadata],oauthEnabled:n.settingsStore.getEffective().oauth.enabled})}),e.httpAdmin.delete("/mcp/api/v1/oauth/clients/:id",o,s,Ae(e,"api"),_n(e),(w,v)=>v.json({revoked:i.revokeClient(String(w.params.id))})),e.httpAdmin.use((w,v,A)=>{if(!Zh.test(w.path)){A();return}n.flowAdapter.getFlows().then(E=>Si(w,Rr(E.flows))).catch(()=>{}).finally(()=>A())}),e.httpAdmin.use(md(n.auditLog)),Va(e,{linkStore:n.linkStore,proposalStore:n.proposalStore,auditLog:n.auditLog,flowAdapter:bt(n.flowAdapter),nodeTypesAdapter:n.nodeTypesAdapter,...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{},rateLimiters:r}),e.httpAdmin.get("/ai-access",o,s,(w,v)=>{v.setHeader("Cache-Control","no-store, no-cache, must-revalidate, max-age=0"),v.setHeader("Pragma","no-cache"),v.setHeader("Expires","0"),v.type("html").status(200).send(Bd({apiBase:fe(e.settings.httpAdminRoot),mcpRpc:Ve(e.settings.httpAdminRoot),editorRoot:pe(e.settings.httpAdminRoot),authTokenKey:mr(e.settings.httpAdminRoot),dashboardPath:Rn(e.settings.httpAdminRoot)}))}),e.httpAdmin.get("/mcp",o,(w,v,A)=>{if(bl(w)){A();return}v.setHeader("Cache-Control","no-store"),v.redirect(302,Rn(e.settings.httpAdminRoot))}),Xh(e,o,s),e.httpAdmin.get("/mcp/api/v1/status",o,s,Js(e,w=>n.linkStore.validateResult(w)),_((w,v)=>{v.json(pn(e,w))})),e.httpAdmin.get("/mcp/api/v1/settings",o,s,Ae(e,"api"),_(ml(e,n.settingsStore))),e.httpAdmin.put("/mcp/api/v1/settings",o,s,Ae(e,"api"),_n(e),We("rest"),_(hl(e,n.settingsStore,n.linkStore))),e.httpAdmin.post("/mcp/api/v1/settings/check-public-url",o,s,Ae(e,"api"),_n(e),We("rest"),_(gl(e)));let C=[o,s,Ae(e,"api"),jt(e),_n(e)];e.httpAdmin.get("/mcp/api/v1/links",...C,_(od(e,n.linkStore))),e.httpAdmin.post("/mcp/api/v1/links",...C,_(sd(e,n.linkStore,n.auditLog))),e.httpAdmin.delete("/mcp/api/v1/links/:id",...C,_(id(e,n.linkStore,n.auditLog)));let L=[o,s,jt(e),Ks(e,w=>n.linkStore.validateResult(w))],N=n.flowAdapter,Ce={nodeTypes:n.nodeTypesAdapter,...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{}};e.httpAdmin.get("/mcp/api/v1/flows",...L,_(fa(N))),e.httpAdmin.get("/mcp/api/v1/flows/:id",...L,_(Zr(N))),e.httpAdmin.get("/mcp/api/v1/nodes/:id",...L,_(Qr(N))),e.httpAdmin.get("/mcp/api/v1/nodes/:id/field/:name",...L,_(eo(N))),e.httpAdmin.get("/mcp/api/v1/search",...L,_(to(N))),e.httpAdmin.get("/mcp/api/v1/node-types",...L,_(no(Ce))),e.httpAdmin.get("/mcp/api/v1/node-types/:type",...L,_(ro(Ce))),e.httpAdmin.get("/mcp/api/v1/palette/search",...L,_(oo(Ce)));let S=e.settings.httpAdminRoot;e.httpAdmin.get("/mcp/api/v1/proposals",...L,_(Wd(n.proposalStore,n.flowAdapter,S))),e.httpAdmin.get("/mcp/api/v1/proposals/:id",...L,_(Gd(n.proposalStore,S,n.flowAdapter))),e.httpAdmin.get("/mcp/api/v1/proposals/:id/diff",...L,_(Yd(n.proposalStore,n.flowAdapter))),e.httpAdmin.post("/mcp/api/v1/proposals",o,s,We("rest"),Ut(e,n.linkStore,"rest"),_(zd(n.proposalStore,n.auditLog,n.flowAdapter,S,n.nodeTypesAdapter))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/approve",...C,We("rest"),_(Kd(n.proposalService,S))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/reject",...C,We("rest"),_(Jd(n.proposalService,S))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/withdraw",o,s,We("rest"),Ut(e,n.linkStore,"rest"),_(Vd(n.proposalStore,n.auditLog,S)));let b=[o,s,(w,v,A)=>{let E=w.headers.authorization;if(!n.settingsStore.getEffective().oauth.enabled||E?.toLowerCase().startsWith("bearer aia_")){if(n.settingsStore.getEffective().oauth.enabled){let $=v.setHeader.bind(v);v.setHeader=((F,le)=>{if(F.toLowerCase()==="www-authenticate"&&typeof le=="string"&&!le.includes("resource_metadata=")){let Ke=`${d().replace(/\/$/,"")}${f.httpNodeRootFallback?"/mcp":""}/.well-known/oauth-protected-resource/mcp`;le+=`, resource_metadata="${Ke}", scope="read propose"`}return $(F,le)})}Ut(e,n.linkStore,"mcp")(w,v,A);return}jt(e)(w,v,()=>u.auth(w,v,A))}];e.httpAdmin.post("/mcp/rpc",...b,We("mcp"),_(Ts(e,n))),e.httpAdmin.get("/mcp/rpc",...b,Vn),e.httpAdmin.delete("/mcp/rpc",...b,Vn),e.httpAdmin.post("/mcp",...b,We("mcp"),_(Ts(e,n))),e.httpAdmin.get("/mcp",...b,Vn),e.httpAdmin.delete("/mcp",...b,Vn),e.httpAdmin.get("/mcp/api/v1/logs",o,s,Ae(e,"api"),jt(e),_(ad(n.auditLog,n.linkStore))),e.httpAdmin.use("/mcp/api/v1",ca()),Qa(e)}a(vl,"registerRoutes");function Qh(e){e.plugins.registerPlugin(Yh,{type:"com55-ai-access",onadd(){vl(e),e.log?.info?.("Node-RED AI Access plugin loaded (dashboard /ai-access, MCP /mcp)")}})}a(Qh,"registerAiAccessPlugin");var eg=Qh;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});