@com55/node-red-ai-access 0.2.0-beta.7 → 0.2.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +54 -0
- package/README.md +67 -21
- package/ai-access.html +1 -1
- package/dist/plugin.cjs +551 -334
- package/dist/plugin.d.cts +35 -1
- package/docs/api/openapi.yaml +189 -33
- package/docs/errors.md +4 -3
- package/docs/mcp-tools.md +9 -7
- package/package.json +2 -1
package/dist/plugin.cjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var mu=Object.create;var nr=Object.defineProperty;var gu=Object.getOwnPropertyDescriptor;var yu=Object.getOwnPropertyNames;var wu=Object.getPrototypeOf,bu=Object.prototype.hasOwnProperty;var a=(e,t)=>nr(e,"name",{value:t,configurable:!0});var W=(e,t)=>()=>(e&&(t=e(e=0)),t);var la=(e,t)=>{for(var n in t)nr(e,n,{get:t[n],enumerable:!0})},ca=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of yu(t))!bu.call(e,o)&&o!==n&&nr(e,o,{get:()=>t[o],enumerable:!(r=gu(t,o))||r.enumerable});return e};var rr=(e,t,n)=>(n=e!=null?mu(wu(e)):{},ca(t||!e||!e.__esModule?nr(n,"default",{value:e,enumerable:!0}):n,e)),vu=e=>ca(nr({},"__esModule",{value:!0}),e);var ku,R,y=W(()=>{"use strict";ku=a(()=>typeof document>"u"?new URL(`file:${__filename}`).href:document.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"?document.currentScript.src:new URL("main.js",document.baseURI).href,"getImportMetaUrl"),R=ku()});function Ts(e,t){let n=Rs.get(e);n?.current.rev!==t.rev&&Rs.set(e,{current:structuredClone(t),previous:n?.current})}function pa(e,t){let n=Rs.get(e);return n?.current.rev===t?n.previous:n?.current}function fa(e){if(ua.has(e))return;ua.add(e);let t=e.getFlows.bind(e);e.getFlows=async()=>{let n=await t();return Ts(e,n),n}}var Rs,ua,Ps=W(()=>{"use strict";y();Rs=new WeakMap,ua=new WeakSet;a(Ts,"recordLiveSnapshot");a(pa,"liveBeforeRevision");a(fa,"observeFlowSnapshots")});function St(e,t,n){let r=n.pretty?`${JSON.stringify(e,null,2)}
|
|
2
2
|
`:`${JSON.stringify(e)}
|
|
3
|
-
`,o=Buffer.from(r,"utf8");if(o.byteLength>t)throw new le(n.kind,o.byteLength,t);return{bytes:o}}function Yi(e,t){let n=(0,Wn.resolve)(t),r=(0,Wn.resolve)(e);if(r!==n&&!r.startsWith(n+Wn.sep))throw new Error("Path escapes .ai-access storage root");return r}function cn(e,t,n){let r=Yi(t,e),o=`${r}.${process.pid}.tmp`;(0,st.writeFileSync)(o,n.bytes,{mode:Or}),(0,st.renameSync)(o,r),(0,st.chmodSync)(r,Or)}function Xi(e,t,n){let r=Yi(t,e);(0,st.appendFileSync)(r,n.bytes,{mode:Or});try{(0,st.chmodSync)(r,Or)}catch{}}var st,Wn,hs,Or,K,Vi,le,se=q(()=>{"use strict";st=require("fs"),Wn=require("path"),hs=448,Or=384,K=15e5,Vi=8192,le=class extends Error{constructor(n,r,o){super(`${n} size ${r} bytes exceeds limit of ${o} bytes`);this.kind=n;this.size=r;this.maxBytes=o;this.name="StoragePayloadTooLargeError"}static{a(this,"StoragePayloadTooLargeError")}code="payload_too_large";statusCode=413};a(ft,"encodeTrustedJson");a(Yi,"assertUnderAiAccess");a(cn,"writeTrustedAtomic");a(Xi,"appendTrusted")});function Kn(e){if(typeof e!="string"||e.length===0||e.length>2048)return;let t;try{t=new URL(e)}catch{return}if(!(t.protocol!=="http:"&&t.protocol!=="https:")&&!(t.username||t.password||t.search||t.hash)&&!(t.pathname!=="/"||/^[a-z][a-z0-9+.-]*:\/\/[^/]*\//i.test(e)))return t.origin.toLowerCase()}function su(e,t){let n=e.getHeader("Vary"),r=String(n??"").split(",").map(o=>o.trim()).filter(Boolean);r.some(o=>o.toLowerCase()===t.toLowerCase())||r.push(t),e.setHeader("Vary",r.join(", "))}function Qi(e){return(t,n,r)=>{if(!tu.test(t.path)){r();return}for(let c of n.getHeaderNames())c.startsWith("access-control-")&&n.removeHeader(c);let o=n.setHeader.bind(n),s=!1;n.setHeader=((c,u)=>!s&&/^access-control-/i.test(c)?n:o(c,u));let i=Kn(t.get("origin")),d=i===void 0?void 0:e().find(c=>c===i),l=d!==void 0;if(su(n,"Origin"),!l){if(t.method==="OPTIONS"&&t.get("access-control-request-method")){n.status(204).end();return}r();return}if(s=!0,n.setHeader("Access-Control-Allow-Origin",d),n.setHeader("Access-Control-Expose-Headers",ou),t.method==="OPTIONS"&&t.get("access-control-request-method")){n.setHeader("Access-Control-Allow-Methods",nu),n.setHeader("Access-Control-Allow-Headers",ru),n.setHeader("Access-Control-Max-Age",String(600)),n.status(204).end();return}s=!1,r()}}function ea(e,t){let n=e.parent,r=n&&typeof n.use=="function"?n:e;r.use(t);let o=r,i=(o._router??o.router)?.stack;if(!Array.isArray(i)||i.length<2)return;let d=i.pop(),l=0;for(;l<i.length;){let c=i[l].name;if(c!=="query"&&c!=="expressInit")break;l+=1}i.splice(l,0,d)}var tu,nu,ru,ou,Dr=q(()=>{"use strict";tu=/^\/mcp(?:\/?$|\/(?:a(?:\/|$)|api(?:\/|$)|rpc\/?$))/,nu="GET, POST, DELETE, OPTIONS",ru="Authorization, Content-Type, Accept, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID",ou="Mcp-Session-Id, WWW-Authenticate, Retry-After, RateLimit, RateLimit-Policy, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset";a(Kn,"canonicalOrigin");a(su,"appendVary");a(Qi,"aiCorsMiddleware");a(ea,"installAiCorsMiddleware")});function jr(e){return e.replace(/([a-z])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/([A-Za-z])([0-9])/g,"$1 $2").replace(/([0-9])([A-Za-z])/g,"$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean).map(t=>t.toUpperCase())}function Vn(e){if(!Array.isArray(e))return;let t=[];for(let n of e){if(typeof n!="string")continue;let r=n.trim().toUpperCase();if(!(!Hr.test(r)||t.includes(r))&&(t.push(r),t.length>=Jn))break}return t}var J,Ft,Hr,Jn,un=q(()=>{"use strict";J="[REDACTED]",Ft=Object.freeze(["PASSWORD","PASSWD","PASS","PWD","PW","PASSPHRASE","SECRET","CLIENT_SECRET","TOKEN","API_KEY","APIKEY","KEY","PRIVATE","CREDENTIAL","AUTH","AUTHORIZATION","BEARER","SESSION","COOKIE","CONNECTION_STRING","CONN_STR","CONN","DSN"]),Hr=/^[A-Za-z0-9_-]{2,40}$/,Jn=100;a(jr,"tokenizeName");a(Vn,"normalizeSecretNamePatterns")});function au(e){return e==="nodes"||e==="flows"||e==="full"}function Ur(e,t){if(!e||typeof e!="object"||Array.isArray(e))return{};let n=e,r={};if(typeof n.enabled=="boolean"&&(r.enabled=n.enabled),typeof n.allowWithoutAdminAuth=="boolean"&&(r.allowWithoutAdminAuth=n.allowWithoutAdminAuth),typeof n.defaultLinkTtlSeconds=="number"&&Number.isFinite(n.defaultLinkTtlSeconds)&&n.defaultLinkTtlSeconds>0&&(r.defaultLinkTtlSeconds=Math.floor(n.defaultLinkTtlSeconds)),typeof n.proposalTtlDays=="number"&&Number.isInteger(n.proposalTtlDays)&&n.proposalTtlDays>=1&&n.proposalTtlDays<=365&&(r.proposalTtlDays=n.proposalTtlDays),au(n.defaultDeploymentType)&&(r.defaultDeploymentType=n.defaultDeploymentType),typeof n.maxActiveLinks=="number"&&Number.isFinite(n.maxActiveLinks)&&n.maxActiveLinks>0&&(r.maxActiveLinks=Math.floor(n.maxActiveLinks)),typeof n.publicBaseUrl=="string")try{r.publicBaseUrl=hn(n.publicBaseUrl)}catch{t?.(`AI Access: invalid publicBaseUrl value ${JSON.stringify(n.publicBaseUrl)} was dropped`)}if(Array.isArray(n.corsAllowedOrigins)){let o=new Set;for(let s of n.corsAllowedOrigins){let i=Kn(s);i?o.add(i):t?.(`AI Access: invalid CORS origin value ${JSON.stringify(s)} was dropped`)}r.corsAllowedOrigins=[...o]}for(let o of["rateLimitWindowMs","rateLimitMaxPerIp","rateLimitMaxPerToken"]){let s=n[o],i=o==="rateLimitWindowMs"?100:1,d=o==="rateLimitWindowMs"?36e5:1e6;typeof s=="number"&&Number.isInteger(s)&&s>=i&&s<=d&&(r[o]=s)}if(n.secretNamePatterns!==void 0){let o=Vn(n.secretNamePatterns);o?(Array.isArray(n.secretNamePatterns)&&o.length<n.secretNamePatterns.length&&t?.("AI Access: invalid or duplicate secretNamePatterns entries were dropped"),r.secretNamePatterns=o):t?.("AI Access: secretNamePatterns must be a list of strings; ignored")}if(n.oauth&&typeof n.oauth=="object"&&!Array.isArray(n.oauth)){let o=n.oauth;r.oauth={},typeof o.enabled=="boolean"&&(r.oauth.enabled=o.enabled);for(let i of["accessTokenTtlSec","refreshIdleTtlSec","dcrMaxClients"]){let d=o[i];typeof d=="number"&&Number.isInteger(d)&&d>0&&d<=31536e3&&(r.oauth[i]=d)}let s=o.refreshMaxLifetimeSec;typeof s=="number"&&Number.isInteger(s)&&s>=0&&s<=31536e4&&(r.oauth.refreshMaxLifetimeSec=s)}return r}function du(e,t){if(!(0,Xn.existsSync)(e))return{};try{let n=(0,Xn.readFileSync)(e,"utf8");return Ur(JSON.parse(n),t)}catch(n){return t?.(`AI Access: config.json is corrupt or unreadable; defaults are used (${String(n)})`),{}}}function Br(e,t){let n={},r={...Yn};for(let s of Object.keys(Yn).filter(i=>i!=="oauth"))n[s]=e[s]!==void 0?"settings.js":t[s]!==void 0?"disk":"default",Object.assign(r,{[s]:e[s]??t[s]??Yn[s]});let o={...r,sources:n};return e.oauth!==void 0||t.oauth!==void 0?(o.oauth={...pn,...t.oauth??{},...e.oauth??{}},n.oauth=e.oauth!==void 0?"settings.js":"disk"):(o.oauth={...pn},n.oauth="default"),o}function ys(e,t){let n=Ur(e??{},t);for(let o of Object.keys(Yn).filter(s=>s!=="oauth"))e&&Object.hasOwn(e,o)&&n[o]===void 0&&Object.assign(n,{[o]:Yn[o]});let r=e?.oauth;if(r&&typeof r=="object"&&!Array.isArray(r)){let o=n.oauth??={};for(let s of Object.keys(pn))Object.hasOwn(r,s)&&o[s]===void 0&&(t?.(`AI Access: settings.js aiAccess.oauth.${s} is invalid; using the default (${String(pn[s])})`),Object.assign(o,{[s]:pn[s]}))}return n}var Xn,gs,iu,pn,Yn,fn,qr=q(()=>{"use strict";Xn=require("fs"),gs=require("path");ve();Dr();un();se();iu=16384,pn={enabled:!1,accessTokenTtlSec:3600,refreshIdleTtlSec:2592e3,refreshMaxLifetimeSec:7776e3,dcrMaxClients:5},Yn={enabled:!0,allowWithoutAdminAuth:!1,defaultLinkTtlSeconds:14400,proposalTtlDays:7,maxActiveLinks:10,defaultDeploymentType:"nodes",publicBaseUrl:"",corsAllowedOrigins:[],rateLimitWindowMs:6e4,rateLimitMaxPerIp:600,rateLimitMaxPerToken:300,secretNamePatterns:[...Ft],oauth:{...pn}};a(au,"isDeploymentType");a(Ur,"sanitizePartial");a(du,"readDiskFile");a(Br,"mergeAiAccessSettings");a(ys,"settingsJsPartial");fn=class{constructor(t,n,r){this.paths=t;this.filePath=(0,gs.join)(t.root,"config.json"),this.settingsJs=ys(n,r),this.disk=du((0,Xn.existsSync)(this.filePath)?this.filePath:(0,gs.join)(t.root,"settings.json"),r),this.cached=Br(this.settingsJs,this.disk)}static{a(this,"SettingsStore")}filePath;disk;settingsJs;cached;getEffective(){return this.cached}getDisk(){return{...this.disk}}update(t){let n=Ur(t);for(let r of Object.keys(this.settingsJs))delete n[r];return this.persist(n),this.disk=n,this.cached=Br(this.settingsJs,this.disk),this.cached}patch(t,n=[]){let r=Ur(t),o={...this.disk,...r,...r.oauth?{oauth:{...this.disk.oauth,...r.oauth}}:{}};for(let s of n)delete o[s];return this.update(o)}persist(t){let n=ft(t,iu,{kind:"settings",pretty:!0});cn(this.paths.root,this.filePath,n)}}});function Wr(e){zr=e}function ta(){return zr}function oe(e){return e.adminAuth!=null&&e.adminAuth!==!1}function Ee(e){return zr?zr.getEffective():Br(ys(e.aiAccess),{})}function Ce(e){let t=Ee(e);return t.enabled?oe(e)?!0:t.allowWithoutAdminAuth:!1}var zr,ke=q(()=>{"use strict";qr();zr=null;a(Wr,"bindSettingsStore");a(ta,"getSettingsStore");a(oe,"isAdminAuthEnabled");a(Ee,"resolveAiAccessSettings");a(Ce,"areAiFeaturesEnabled")});function Ae(e){let t=`/${e??""}`.replace(/\/{2,}/g,"/");for(;t.length>1&&t.endsWith("/");)t=t.slice(0,-1);return t}function Se(e){let t=Ae(e);return t==="/"?"/mcp/api/v1":`${t}/mcp/api/v1`}function Zn(e){let t=Ae(e);return t==="/"?"/ai-access":`${t}/ai-access`}function ht(e){let t=Ae(e);return t==="/"?"/mcp/rpc":`${t}/mcp/rpc`}function lu(e){let t=e.authInfo?.scope;if(t)return Array.isArray(t)?t:[t];let n=e.user?.permissions;return n?Array.isArray(n)?n:[n]:[]}function mt(e){if(e.auth)return e.auth.scopes.includes("propose")?"propose":e.auth.scopes.includes("read")?"read":null;let t=lu(e);return t.includes("*")?"*":t.includes("read")?"read":null}function cu(e,t){return!t||!e?{view:!0,manageLinks:!1,approveProposals:!1}:e==="*"?{view:!0,manageLinks:!0,approveProposals:!0}:{view:!0,manageLinks:!1,approveProposals:!1}}function na(e,t={error:"Authentication required",code:"auth_required"},n){let r=e.end.bind(e),o=!0;return e.end=((...s)=>o&&e.statusCode===401&&!e.headersSent?(o=!1,e.removeHeader("Content-Length"),e.setHeader("Content-Type","application/json; charset=utf-8"),n&&e.setHeader("WWW-Authenticate",n),r(JSON.stringify(t))):r(...s)),()=>{o=!1,e.end=r}}function Ie(e,t){let n="read";return(r,o,s)=>{if(!oe(e.settings)){s();return}let i=t==="api"?na(o):null;e.auth.needsPermission(n)(r,o,d=>{if(i?.(),d){s(d);return}if(!r.user){if(t==="page"){o.redirect(Ae(e.settings.httpAdminRoot));return}o.status(401).json({error:"Authentication required",code:"auth_required"});return}if(!mt(r)){o.status(401).json({error:"Insufficient permissions",code:"insufficient_permissions"});return}s()})}}function Gr(e,t){let n=oe(e.settings),r=Ee(e.settings),o=Ce(e.settings),s=null;return n?s=mt(t):o&&(s="*"),{adminAuthEnabled:n,aiFeaturesEnabled:o,allowWithoutAdminAuth:r.allowWithoutAdminAuth,permission:s,capabilities:cu(s,o)}}function mn(e){return(t,n,r)=>{if(!Ce(e.settings)){n.status(403).json({error:"AI Access features are disabled. Enable adminAuth, or set aiAccess.allowWithoutAdminAuth to true for local-only use.",code:"ai_features_disabled"});return}r()}}function Qn(e){return(t,n,r)=>{if(!oe(e.settings)){if(Ce(e.settings)){r();return}n.status(403).json({error:"AI Access features are disabled",code:"ai_features_disabled"});return}if(mt(t)!=="*"){n.status(403).json({error:"Full admin permission (*) is required for this action",code:"insufficient_permissions"});return}r()}}function uu(e){return e.startsWith("aia_")}function pu(e){return uu(e)?!0:e.length<24}function fu(e){if(e.length<24||e.startsWith("aia_")&&e.length<40)return"The token may be truncated \u2014 ask the user for the full access-link token."}function ra(e,t,n,r){if(!oe(e.settings)){r();return}let o={error:xe,code:qe},s=na(n,o,ce);e.auth.needsPermission("read")(t,n,i=>{if(s(),i){r(i);return}if(!n.headersSent){if(!t.user||!mt(t)){n.setHeader("WWW-Authenticate",ce),n.status(401).json(o);return}r()}})}function oa(e,t,n,r,o){let s=e.headers.authorization;if(typeof s!="string"||!/^Bearer\s+\S+/i.test(s.trim()))return"continue";let d=/^Bearer\s+(\S+)$/i.exec(s.trim())?.[1];if(!d)return"continue";let l=n(d);if(l.status==="active")return l.link.kind!==r?(nr(t,r,l.link.kind),"wrong_kind"):(e.accessLink=l.link,"ok");if(l.status==="expired")return tr(e,t,o,l.expiresAt),"invalid";if(pu(d)){t.setHeader("WWW-Authenticate",ce);let c=fu(d);return t.status(401).json({error:Ot,code:er,...c?{hint:c}:{}}),"invalid"}return"continue"}function sa(e,t,n="rest"){return(r,o,s)=>{if(!Ce(e.settings)){o.status(403).json({error:"AI Access features are disabled. Enable adminAuth, or set aiAccess.allowWithoutAdminAuth to true for local-only use.",code:"ai_features_disabled"});return}let i=oa(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||ra(e,r,o,s)}}function ia(e,t,n="rest"){return(r,o,s)=>{let i=oa(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||ra(e,r,o,s)}}var Be=q(()=>{"use strict";$t();ke();a(Ae,"normalizeHttpAdminRoot");a(Se,"mcpApiBase");a(Zn,"dashboardPath");a(ht,"mcpRpcPath");a(lu,"scopesFromRequest");a(mt,"resolvePermission");a(cu,"capabilitiesFor");a(na,"interceptNeedsPermissionUnauthorized");a(Ie,"requireAdminSession");a(Gr,"describeAccess");a(mn,"requireAiFeatures");a(Qn,"requireStarPermission");a(uu,"looksLikeAccessLinkToken");a(pu,"looksLikeFailedAccessLinkAttempt");a(fu,"truncatedTokenHint");a(ra,"continueAsAdminSessionOrAccessLinkMissing");a(oa,"tryAccessLinkBearer");a(sa,"requireAdminReadOrAccessLink");a(ia,"requireAdminSessionOrAccessLink")});function hu(e,t){let n=Ae(e);return`${n==="/"?"/mcp/a":`${n}/mcp/a`}/${t}`}function mu(e){return!!e.app.get("trust proxy")}function aa(e,t){let n=e.get(t);if(typeof n!="string"||n.length===0)return;let r=n.split(",")[0]?.trim();return r&&r.length>0?r:void 0}function gu(e){if(typeof e!="string"||e.trim().length===0)return;let t;try{t=new URL(e.trim())}catch{return}if(!(t.protocol!=="http:"&&t.protocol!=="https:"))return`${t.origin}${t.pathname.replace(/\/+$/,"")}`}function Mt(e){try{let n=new URL(`http://${e.includes(":")&&!e.startsWith("[")?`[${e}]`:e}`).hostname.replace(/^\[|\]$/g,"");if(!(0,ws.isIP)(n))return;if(n.startsWith("::ffff:")){let r=n.slice(7);if((0,ws.isIP)(r)===4)return r;let o=r.split(":");if(o.length===2){let s=parseInt(o[0],16)*65536+parseInt(o[1],16);return[24,16,8,0].map(i=>s>>>i&255).join(".")}}return n}catch{return}}function hn(e){if(e==="")return"";let t="publicBaseUrl must be an HTTP(S) origin with no path, query, fragment, or credentials";if(typeof e!="string")throw new Error(t);let n;try{n=new URL(e.trim())}catch{throw new Error(t)}if(n.protocol!=="https:"&&n.protocol!=="http:"||!/^https?:\/\/[^/?#]+\/?$/i.test(e.trim())||n.pathname!=="/"||n.search||n.hash||n.username||n.password||e.includes("?")||e.includes("#"))throw new Error(t);return n.origin}function Kr(e){bs=gu(e)}function la(e){if(typeof e!="string"||e.length===0||e.length>260)return!1;let t,n,r=/^(\[[^\]]*\])(?::(\d+))?$/.exec(e);if(r){if(t=r[1],n=r[2],!bu.test(t))return!1;try{new URL(`http://${t}/`)}catch{return!1}}else{let o=/^([^:]+)(?::(\d+))?$/.exec(e);if(!o||(t=o[1],n=o[2],t.length>253)||!wu.test(t)&&!yu.test(t))return!1}if(n!==void 0){if(!/^\d{1,5}$/.test(n))return!1;let o=Number(n);if(o<1||o>65535)return!1}return!0}function ua(e){let t=e?.trim().toLowerCase();return t==="http"||t==="https"?t:void 0}function vu(e){let t=e.socket;return t?.encrypted?"https":t?"http":ua(e.protocol)??"http"}function vs(e){if(bs)return bs;let t=mu(e),n=t?aa(e,"x-forwarded-host"):void 0,r=e.get("host")?.trim(),o=la(n)?n:la(r)?r:void 0;if(!o)return;let s=(t?ua(aa(e,"x-forwarded-proto")):void 0)??vu(e);try{return new URL(`${s}://${o}`).origin}catch{return}}function rr(e,t){let n=vs(e);return n?`${n}${t}`:t}function pa(e,t,n){return rr(e,hu(t,n))}function ks(e){return e.replace(/\/mcp\/a\/[^/?#]+/gi,"/mcp/a/<redacted>")}var ws,gn,ca,bs,da,yu,wu,bu,ve=q(()=>{"use strict";ws=require("net");Be();gn=8192,ca=1800*1e3;a(hu,"mcpAccessLinkPath");a(mu,"isTrustProxyEnabled");a(aa,"firstForwardedValue");a(gu,"normalizePublicBaseUrl");a(Mt,"canonicalizeIp");a(hn,"validateSettingsPublicBaseUrl");a(Kr,"bindPublicBaseUrl");da="[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?",yu=new RegExp(`^${da}(?:\\.${da})*$`),wu=/^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/,bu=/^\[[0-9A-Fa-f:.]+\]$/;a(la,"isValidHostHeader");a(ua,"validScheme");a(vu,"connectionScheme");a(vs,"requestPublicOrigin");a(rr,"absolutizePath");a(pa,"buildAccessLinkUrl");a(ks,"redactAccessLinkTokenInPath")});function Ru(e){if(!e||e.trim().length===0)return{kind:"missing"};let t=e.trim();if(/^Basic\s+/i.test(t))return{kind:"basic"};let n=/^Bearer\s+(\S+)$/i.exec(t);return n?.[1]?{kind:"bearer",token:n[1]}:{kind:"other"}}function As(e,t,n,r){e.setHeader("WWW-Authenticate",ce),e.status(401).json({error:t,code:n,...r?{hint:r}:{}})}function Pu(e){let t=e??"";for(;t.endsWith("/");)t=t.slice(0,-1);return`${t}/ai-access#links`}function _u(e,t,n,r){let o=rr(e,Pu(t));return{error:ku,code:Su,...n?{expiredAt:n}:{},accessLinksUrl:o,hint:r??`Ask the user to create a new access link in Node-RED on the /ai-access dashboard \u2192 Access tokens (${o}).`}}function tr(e,t,n,r,o){t.setHeader("WWW-Authenticate",ce),t.status(401).json(_u(e,n,r,o))}function Eu(e){if(e.length<24||e.startsWith("aia_")&&e.length<40)return"The token may be truncated \u2014 ask the user for the full access-link token."}function Jr(e){return e==="get_link"?"Please open Node-RED \u2192 /ai-access \u2192 Access tokens and create a get_link access URL, then send me that /mcp/a/<token> link.":`Please open Node-RED \u2192 /ai-access \u2192 Access tokens and create a kind=${e} token, then send me the Bearer token (not a get_link path URL).`}function Iu(e,t){let n=Cu;return e==="get_link"?`This URL requires a get_link token. This token is kind=${t} \u2014 ${n} for a get_link access URL (/mcp/a/<token>/\u2026). What to tell the user: ${Jr("get_link")}`:e==="rest"?`This REST endpoint requires a rest token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${Jr("rest")}`:`This MCP endpoint requires an mcp token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${Jr("mcp")}`}function nr(e,t,n){e.setHeader("WWW-Authenticate",ce),e.status(403).json({error:`Access link kind mismatch: expected ${t}, got ${n}`,code:Tu,expectedKind:t,actualKind:n,hint:Iu(t,n),tellUser:Jr(t)})}function Dt(e,t,n){return(r,o,s)=>{if(!Ce(e.settings)){o.status(403).json({error:"AI Access features are disabled. Enable adminAuth, or set aiAccess.allowWithoutAdminAuth to true for local-only use.",code:"ai_features_disabled"});return}let i=Ru(r.headers.authorization);if(i.kind==="basic"){As(o,Au,xu);return}if(i.kind==="missing"||i.kind==="other"){As(o,xe,qe);return}let d=t.validateResult(i.token);if(d.status==="expired"){tr(r,o,e.settings.httpAdminRoot,d.expiresAt);return}if(d.status!=="active"){As(o,Ot,er,Eu(i.token));return}if(d.link.kind!==n){nr(o,n,d.link.kind);return}r.accessLink=d.link,s()}}var Ot,ku,xe,Au,ce,qe,er,Su,xu,Tu,Cu,$t=q(()=>{"use strict";ve();ke();Ot="Access link is invalid, revoked, or no longer available (it may have expired, or Node-RED may have restarted). Ask the user for a new access link.",ku="Access link token has expired. Ask the user for a new access link.",xe="Access link Bearer token is missing. Ask the user for a new access link and send Authorization: Bearer <token>.",Au="Authorization must use the Bearer scheme (Authorization: Bearer <access-link-token>), not Basic. Ask the user for a new access link.",ce='Bearer realm="node-red-ai-access", charset="UTF-8"',qe="access_link_missing",er="access_link_invalid",Su="token_expired",xu="bearer_scheme_required",Tu="wrong_token_kind";a(Ru,"parseAuthorizationHeader");a(As,"sendUnauthorized");a(Pu,"accessLinksPagePath");a(_u,"tokenExpiredBody");a(tr,"sendTokenExpired");a(Eu,"truncatedBearerHint");Cu="ask the owner: Node-RED /ai-access \u2192 Access tokens \u2192 create kind=rest/mcp (or get_link)";a(Jr,"whatToTellUserWrongTokenKind");a(Iu,"wrongTokenKindHint");a(nr,"sendWrongTokenKind");a(Dt,"requireAccessLink")});var te,X,Le,ze,Yr,Te=q(()=>{"use strict";te=class extends Error{static{a(this,"FlowRevisionConflictError")}code="version_mismatch";statusCode=409;expectedRev;currentRev;context;constructor(t,n,r="updateFlow"){super(`Flow revision conflict in ${r}: expectedRev '${t}' does not match current rev '${n}'. The flows changed since you last read them \u2014 re-read flows, re-apply your change, and retry.`),this.name="FlowRevisionConflictError",this.expectedRev=t,this.currentRev=n,this.context=r}},X=class extends Error{static{a(this,"FlowNotFoundError")}code="flow_not_found";statusCode=404;flowId;constructor(t){super(`Flow '${t}' was not found`),this.name="FlowNotFoundError",this.flowId=t}},Le=class extends Error{static{a(this,"AdapterUnavailableError")}code="adapter_unavailable";statusCode=502;constructor(t){super(t),this.name="AdapterUnavailableError"}},ze=class extends Error{static{a(this,"PluginMisconfiguredError")}code="plugin_misconfigured";statusCode=502;constructor(t){super(t),this.name="PluginMisconfiguredError"}},Yr="Node-RED AI Access could not use the in-process flow runtime and the HTTP Admin API fallback returned Unauthorized. The plugin is misconfigured \u2014 tell the admin to ensure the plugin loads inside Node-RED so RED.runtime.flows is available (do not rely on unauthenticated localhost HTTP when adminAuth is enabled)."});function We(e,t){if(!e||!Lu.test(e)||e.includes(".."))throw new Error(`Invalid ${t}`);return e}function Xr(e){if(!e||!Nu.test(e)||e.includes(".."))throw new Error("Invalid proposal id");return e}function Ss(e,t){let n=(0,Ht.basename)(t);if(!n||n!==t||n.includes(".."))throw new Error("Invalid file name");let r=(0,Ht.resolve)(e),o=(0,Ht.resolve)(r,n);if(o!==r&&!o.startsWith(r+Ht.sep))throw new Error("Path escapes storage directory");return o}function fa(e,t=1e5){if(typeof e=="string")return e.replace(/\0/g,"").slice(0,t)}function it(e){let t=e&&typeof e=="object"&&!Array.isArray(e)?e:{},n={};for(let o of Fu){if(!(o in t))continue;let s=t[o];if(o==="disabled"&&typeof s=="boolean"){n[o]=s;continue}if(o==="env"&&Array.isArray(s)){let d=JSON.stringify(s);d.length<=5e5&&(n[o]=JSON.parse(d));continue}let i=fa(s,1e4);i!==void 0&&(n[o]=i)}for(let o of["nodes","configs"]){let s=t[o];if(!Array.isArray(s))continue;let i=JSON.stringify(s),d=Buffer.byteLength(i,"utf8");if(d>K)throw new le(`flow.${o}`,d,K);n[o]=JSON.parse(i)}let r=Buffer.byteLength(JSON.stringify(n),"utf8");if(r>K)throw new le("flow",r,K);return n}function ha(e){let t={};for(let n of Ou){if(!(n in e))continue;let r=e[n];if($u.has(n)){if(typeof r=="number"&&Number.isFinite(r))t[n]=r;else if(typeof r=="string"&&r.trim()!==""){let s=Number(r);Number.isFinite(s)&&(t[n]=s)}continue}let o=fa(r,2e3);o!==void 0&&(t[n]=o)}return t}var Ht,Lu,Nu,Fu,Ou,$u,Zr=q(()=>{"use strict";Ht=require("path");se();Lu=/^[A-Za-z0-9_.-]+$/,Nu=/^prop-[A-Za-z0-9_-]+$/,Fu=["id","type","label","disabled","info","env"],Ou=["proposalId","flowId","expectedRev","resultRev","reason","code","currentRev","title","displayName","flowLabel","linkId","label","expiresAt","createdAt","ttlSeconds","kind","redactions","restoredRedactions","operation"],$u=new Set(["ttlSeconds","redactions","restoredRedactions"]);a(We,"assertSafeSegment");a(Xr,"assertSafeProposalId");a(Ss,"resolveUnderDir");a(fa,"sanitizeString");a(it,"sanitizeFlowTab");a(ha,"sanitizeAuditDetails")});function U(e){return e==="pending"||e==="conflict"}function to(e,t){return`oauth-${(0,eo.createHash)("sha256").update(`${e}\0${t}`).digest("hex")}`}function Ut(e){return e.kind==="oauth"?to(e.clientId,e.user):e.linkId}function Mu(e){return e==="pending"||e==="conflict"||e==="approved"||e==="rejected"||e==="failed"||e==="withdrawn"||e==="expired"||e==="superseded"}function Ge(e,t){return typeof e=="string"&&e.length>0?e.replace(/\0/g,"").slice(0,t):void 0}function Hu(e){if(!e||typeof e!="object"||Array.isArray(e))return;let t=e,n=Ge(t.rev,200),r=t.state;if(!n||r!=="clean"&&r!=="merged"&&r!=="conflict")return;let o=[];if(Array.isArray(t.conflicts))for(let d of t.conflicts.slice(0,200)){if(!d||typeof d!="object")continue;let l=d,c=Ge(l.kind,64),u=Ge(l.message,500);if(!c||!u)continue;let p={kind:c,message:u,choices:Array.isArray(l.choices)?l.choices.filter(g=>g==="live"||g==="proposed"):[]},f=Ge(l.nodeId,200);f&&(p.nodeId=f);let h=Ge(l.nodeType,200);h&&(p.nodeType=h);let m=Ge(l.nodeName,120);m&&(p.nodeName=m),Array.isArray(l.fields)&&(p.fields=l.fields.filter(g=>typeof g=="string"&&g.length>0).slice(0,100).map(g=>g.replace(/\0/g,"").slice(0,200))),o.push(p)}let s={rev:n,state:r,conflicts:o,liveChangedCount:typeof t.liveChangedCount=="number"&&Number.isFinite(t.liveChangedCount)?Math.max(0,Math.floor(t.liveChangedCount)):0,checkedAt:Ge(t.checkedAt,64)??""},i=Ge(t.mergedHash,64);return i&&/^[0-9a-f]+$/.test(i)&&(s.mergedHash=i),s}function ju(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e.slice(0,200)){if(!n||typeof n!="object")continue;let{nodeId:r,property:o}=n;typeof r!="string"||typeof o!="string"||t.push({nodeId:r.replace(/\0/g,"").slice(0,200),property:o.replace(/\0/g,"").slice(0,200)})}return t.length>0?t:void 0}function ma(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e.slice(0,Du)){if(!n||typeof n!="object"||Array.isArray(n))continue;let r=n;if(typeof r.code!="string"||!r.code||typeof r.error!="string"||!r.error)continue;let o={code:r.code.replace(/\0/g,"").slice(0,80),error:r.error.replace(/\0/g,"").slice(0,500)};typeof r.nodeId=="string"&&r.nodeId&&(o.nodeId=r.nodeId.replace(/\0/g,"").slice(0,200)),typeof r.type=="string"&&r.type&&(o.type=r.type.replace(/\0/g,"").slice(0,200)),typeof r.property=="string"&&r.property&&(o.property=r.property.replace(/\0/g,"").slice(0,200)),typeof r.configType=="string"&&r.configType&&(o.configType=r.configType.replace(/\0/g,"").slice(0,200)),Array.isArray(r.candidates)&&(o.candidates=r.candidates.filter(s=>typeof s=="string"&&s.length>0).slice(0,20).map(s=>s.replace(/\0/g,"").slice(0,200))),t.push(o)}return t.length>0?t:void 0}var eo,gt,ga,me,Y,jt,Re,Du,Ke,Qr,Je=q(()=>{"use strict";eo=require("crypto"),gt=require("fs"),ga=require("path");Zr();se();a(U,"isOpenProposalStatus");a(to,"oauthOwnerId");a(Ut,"proposalOwnerId");me=class extends Error{static{a(this,"ProposalNotFoundError")}code="proposal_not_found";statusCode=404;constructor(t){super(`Proposal '${t}' was not found`),this.name="ProposalNotFoundError"}},Y=class extends Error{static{a(this,"ProposalNotPendingError")}code="proposal_not_pending";statusCode=409;constructor(t,n){super(`Proposal '${t}' is '${n}', not pending`),this.name="ProposalNotPendingError"}},jt=class extends Error{static{a(this,"ProposalNotOwnedError")}code="proposal_not_owned";statusCode=403;constructor(t){super(`Proposal '${t}' was not created by this access link`),this.name="ProposalNotOwnedError"}},Re=class extends Error{static{a(this,"ProposalConcurrentApproveError")}code="proposal_approve_in_progress";statusCode=409;constructor(t){super(`Proposal '${t}' is already being approved`),this.name="ProposalConcurrentApproveError"}};a(Mu,"isProposalStatus");Du=50;a(Ge,"cleanText");a(Hu,"sanitizeRebaseInfo");Ke=class extends Error{static{a(this,"ProposalConflictError")}code="proposal_conflict";statusCode=409;conflicts;constructor(t,n){super(`Proposal '${t}' conflicts with changes made in the editor since it was submitted; resolve the conflicts before approving.`),this.name="ProposalConflictError",this.conflicts=n}};a(ju,"sanitizeRestoredSecrets");a(ma,"sanitizeProposalWarnings");Qr=class{constructor(t,n={}){this.paths=t;this.now=n.now??(()=>Date.now()),this.randomId=n.randomId??(()=>(0,eo.randomBytes)(12).toString("hex")),this.maxProposalBytes=n.maxProposalBytes??K}static{a(this,"ProposalStore")}inFlight=new Set;now;randomId;maxProposalBytes;fileFor(t){let n;try{n=Xr(t)}catch{throw new me(t)}return Ss(this.paths.proposals,`${n}.json`)}toDiskRecord(t){let n=Xr(t.id),r=We(String(t.expectedRev).slice(0,200),"expectedRev"),o=We(String(t.payload.flowId).slice(0,200),"flowId"),s=it(t.payload.flow),i=t.baseFlow!==void 0?it(t.baseFlow):void 0,d=t.createdBy.kind==="oauth"?{kind:"oauth",clientId:We(String(t.createdBy.clientId).slice(0,200),"clientId"),user:String(t.createdBy.user).replace(/\0/g,"").slice(0,200)}:{kind:"access-link",linkId:We(String(t.createdBy.linkId).slice(0,200),"linkId"),...typeof t.createdBy.label=="string"?{label:t.createdBy.label.replace(/\0/g,"").slice(0,200)}:{}},l={id:n,type:"replace_flow",status:Mu(t.status)?t.status:"pending",expectedRev:r,payload:{flowId:o,flow:s},createdBy:d,createdAt:String(t.createdAt).slice(0,64),updatedAt:String(t.updatedAt).slice(0,64)};i!==void 0&&(l.baseFlow=i),typeof t.title=="string"&&t.title.length>0&&(l.title=t.title.replace(/\0/g,"").slice(0,80).trim(),l.title||delete l.title);let c=ma(t.warnings);c&&(l.warnings=c);let u=ju(t.restoredSecrets);if(u&&(l.restoredSecrets=u),t.deployPending&&U(l.status)){let f=Ge(t.deployPending.mergedHash,64),h=Ge(t.deployPending.reason,500);f&&/^[0-9a-f]+$/.test(f)&&h&&(l.deployPending={mergedHash:f,reason:h},t.deployPending.queueReleased===!0&&(l.deployPending.queueReleased=!0))}let p=Hu(t.rebase);if((p&&U(l.status)||p&&p.state!=="conflict")&&(l.rebase=p),t.decidedBy)if(t.decidedBy.kind==="access-link"){let f={kind:"access-link",linkId:We(String(t.decidedBy.linkId).slice(0,200),"linkId")};typeof t.decidedBy.label=="string"&&(f.label=t.decidedBy.label.replace(/\0/g,"").slice(0,200)),l.decidedBy=f}else{let f={kind:"admin"};typeof t.decidedBy.username=="string"&&(f.username=t.decidedBy.username.replace(/\0/g,"").slice(0,200)),l.decidedBy=f}return typeof t.reason=="string"&&(l.reason=t.reason.replace(/\0/g,"").slice(0,2e3)),typeof t.resultRev=="string"&&(l.resultRev=We(t.resultRev.slice(0,200),"resultRev")),l}writeAtomic(t,n){let r=this.fileFor(t),o=this.toDiskRecord(n),s=ft(o,this.maxProposalBytes,{kind:"proposal",pretty:!0});cn(this.paths.root,r,s)}create(t){let n=new Date(this.now()).toISOString(),r=Xr(`prop-${this.randomId()}`),o={id:r,type:"replace_flow",status:"pending",expectedRev:t.expectedRev,payload:{flowId:t.payload.flowId,flow:t.payload.flow},baseFlow:t.baseFlow,title:t.title,createdBy:t.createdBy,createdAt:n,updatedAt:n},s=ma(t.warnings);return s&&(o.warnings=s),t.restoredSecrets?.length&&(o.restoredSecrets=t.restoredSecrets),this.writeAtomic(r,o),this.get(r)}get(t){let n=this.fileFor(t);if(!(0,gt.existsSync)(n))throw new me(t);return JSON.parse((0,gt.readFileSync)(n,"utf8"))}list(t){if(!(0,gt.existsSync)(this.paths.proposals))return[];let n=(0,gt.readdirSync)(this.paths.proposals).filter(o=>o.endsWith(".json")),r=[];for(let o of n){let s=(0,ga.basename)(o);if(!(s!==o||s.includes("..")))try{let i=JSON.parse((0,gt.readFileSync)(Ss(this.paths.proposals,s),"utf8"));if(t?.linkId&&Ut(i.createdBy)!==t.linkId||t?.status&&i.status!==t.status||t?.flowId&&i.payload.flowId!==t.flowId)continue;r.push(i)}catch{}}return r.sort((o,s)=>o.createdAt<s.createdAt?1:-1)}beginApprove(t){if(this.inFlight.has(t))throw new Re(t);let n=this.get(t);if(!U(n.status))throw new Y(t,n.status);return this.inFlight.add(t),n}releaseLock(t){this.inFlight.delete(t)}recordDeployPending(t,n){let r=this.get(t);if(!U(r.status))throw new Y(t,r.status);return n?r.deployPending=n:delete r.deployPending,r.status="pending",r.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,r),this.get(t)}markApproved(t,n,r){let o=this.get(t);return o.status="approved",delete o.deployPending,o.decidedBy=n,o.resultRev=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markRejected(t,n,r){if(this.inFlight.has(t))throw new Re(t);let o=this.get(t);return o.status="rejected",n&&(o.decidedBy=n),o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markFailed(t,n,r){let o=this.get(t);return o.status="failed",n&&(o.decidedBy=n),o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markWithdrawn(t,n,r){if(this.inFlight.has(t))throw new Re(t);let o=this.get(t);if(!U(o.status))throw new Y(t,o.status);return o.status="withdrawn",o.decidedBy=n,o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markClosed(t,n,r,o){let s=this.get(t);if(!U(s.status))throw new Y(t,s.status);return s.status=n,o&&(s.decidedBy=o),s.reason=r,s.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,s),this.get(t)}recordRebase(t,n){let r=this.get(t);if(!U(r.status))return r;let o=n.state==="conflict"?"conflict":"pending",s=r.rebase;return r.status===o&&s!==void 0&&s.rev===n.rev&&s.state===n.state&&s.mergedHash===n.mergedHash&&s.liveChangedCount===n.liveChangedCount&&JSON.stringify(s.conflicts)===JSON.stringify(n.conflicts)?r:(r.status=o,r.rebase=n,r.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,r),this.get(t))}updateBody(t,n){let r=this.get(t);if(!U(r.status))throw new Y(t,r.status);if(this.inFlight.has(t))throw new Re(t);return r.baseFlow=n.baseFlow,r.payload={flowId:r.payload.flowId,flow:n.flow},n.expectedRev!==void 0&&(r.expectedRev=n.expectedRev),n.title!==void 0&&(r.title=n.title),n.restoredSecrets!==void 0&&(n.restoredSecrets.length>0?r.restoredSecrets=n.restoredSecrets:delete r.restoredSecrets),n.warnings!==void 0&&(n.warnings.length>0?r.warnings=n.warnings:delete r.warnings),r.status="pending",delete r.rebase,r.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,r),this.get(t)}isApproving(t){return this.inFlight.has(t)}}});function ya(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new Error(`${t} must be a node object`);let n=e;if(typeof n.id!="string"||n.id.length===0)throw new Error(`${t} needs a non-empty string id`);if(typeof n.type!="string"||n.type.length===0)throw new Error(`${t} '${n.id}' needs a non-empty string type`);return n}function no(e,t,n){let r=e.find(d=>d.id===t);if(!r||r.type!=="tab"&&r.type!=="subflow")throw new X(t);let o={...r,id:t,type:r.type};delete o.nodes,delete o.configs,delete o.subflows,n.label!==void 0&&(o.label=n.label),n.disabled!==void 0&&(o.disabled=n.disabled),n.info!==void 0&&(o.info=n.info),n.env!==void 0&&(o.env=n.env);let s=[];for(let[d,l]of(n.nodes??[]).entries())s.push({...ya(l,`flowData.nodes[${d}]`),z:t});for(let[d,l]of(n.configs??[]).entries())s.push({...ya(l,`flowData.configs[${d}]`),z:t});return[...e.filter(d=>d.id!==t&&d.z!==t),o,...s]}function or(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new Error(`Expected ${t} to be an object with rev and flows`);let n=e;if(typeof n.rev!="string"||n.rev.length===0)throw new Error(`Expected ${t}.rev to be a non-empty string`);if(!Array.isArray(n.flows))throw new Error(`Expected ${t}.flows to be an array`);return{rev:n.rev,flows:n.flows}}function yn(e,t){if(Array.isArray(t)){let d=t.find(u=>u&&typeof u=="object"&&u.id===e&&(u.type==="tab"||u.type==="subflow"));if(!d)throw new X(e);let l=t.filter(u=>u&&typeof u=="object"&&u.id!==e&&u.z===e),c={id:e,type:d.type,nodes:l};return typeof d.label=="string"&&(c.label=d.label),typeof d.disabled=="boolean"&&(c.disabled=d.disabled),typeof d.info=="string"&&(c.info=d.info),c}if(!t||typeof t!="object")throw new X(e);let n=t,r=typeof n.id=="string"&&n.id.length>0?n.id:e,o=typeof n.type=="string"&&n.type.length>0?n.type:"tab",s=Array.isArray(n.nodes)?n.nodes:[],i={...n,id:r,type:o,nodes:s};return Array.isArray(n.configs)&&(i.configs=n.configs),i}var ro=q(()=>{"use strict";Te();a(ya,"asFlowNode");a(no,"replaceFlowTabInFlatFlows");a(or,"assertFlowsSnapshot");a(yn,"normalizeFlowTab")});function wa(e,t=new xs){return oo in e?e:{kind:e.kind,[oo]:t,async getFlows(){let r=await e.getFlows();return t.record(r),r},async getFlow(r){let o=await e.getFlow(r);if(!t.has(o.rev))try{t.record(await e.getFlows())}catch{}return o},async updateFlow(r){return e.updateFlow(r)}}}function Ts(e){return oo in e?e[oo]:void 0}function so(e,t){return{kind:t,async getFlows(){return{rev:e.rev,flows:JSON.parse(JSON.stringify(e.flows))}},async getFlow(n){let r=JSON.parse(JSON.stringify(e.flows)),o=r.find(l=>l.id===n&&(l.type==="tab"||l.type==="subflow"));if(!o)throw new X(n);let s=[],i=[];for(let l of r){if(l.id===n||l.z!==n)continue;(l.type!=="group"&&l.x===void 0&&l.y===void 0&&l.wires===void 0?i:s).push(l)}let d={id:n,type:o.type,nodes:s};for(let l of["label","disabled","info","env"])o[l]!==void 0&&(d[l]=o[l]);return i.length>0&&(d.configs=i),{rev:e.rev,flow:d}},async updateFlow(){throw new Error("snapshot adapter is read-only")}}}var Uu,xs,oo,Rs=q(()=>{"use strict";Te();Uu=24,xs=class{constructor(t=Uu){this.maxRevs=t}static{a(this,"RevHistory")}byRev=new Map;record(t){if(!(!t||typeof t.rev!="string"||!Array.isArray(t.flows))&&!this.byRev.has(t.rev))for(this.byRev.set(t.rev,{rev:t.rev,flows:JSON.parse(JSON.stringify(t.flows))});this.byRev.size>this.maxRevs;){let n=this.byRev.keys().next().value;if(n===void 0)break;this.byRev.delete(n)}}get(t){let n=this.byRev.get(t);return n?{rev:n.rev,flows:JSON.parse(JSON.stringify(n.flows))}:void 0}has(t){return this.byRev.has(t)}},oo=Symbol.for("ai-access.revHistory");a(wa,"withRevHistory");a(Ts,"revHistoryOf");a(so,"snapshotAdapter")});function Cs(e){return e?Bu.has(e)?!0:e.startsWith("subflow:"):!1}function yt(e,t="node.type"){if(e==null||e==="")throw new b(`${t} is required`,"missing_node_type");if(typeof e!="string")throw new b(`${t} must be a non-empty string (got ${typeof e})`,"invalid_node_type",{details:{type:e,got:typeof e}});if(!e.trim())throw new b(`${t} is required`,"missing_node_type");return e}function Vu(e,t){if(typeof e!="string"||typeof t!="string")return Number.MAX_SAFE_INTEGER;let n=e.toLowerCase(),r=t.toLowerCase();if(n===r)return 0;let o=n.length,s=r.length;if(o===0)return s;if(s===0)return o;let i=new Array(s+1),d=new Array(s+1);for(let l=0;l<=s;l++)i[l]=l;for(let l=1;l<=o;l++){d[0]=l;let c=n.charAt(l-1);for(let u=1;u<=s;u++){let p=c===r.charAt(u-1)?0:1;d[u]=Math.min((d[u-1]??0)+1,(i[u]??0)+1,(i[u-1]??0)+p)}for(let u=0;u<=s;u++)i[u]=d[u]??0}return i[s]??s}function Yu(e,t){let n=e.toLowerCase(),r=t.toLowerCase(),o=Math.min(n.length,r.length),s=0;for(;s<o&&n.charAt(s)===r.charAt(s);)s+=1;return s}function Zu(e){return Math.max(2,Math.floor(e.length/3))}function Is(e,t,n=5){if(typeof e!="string"||!e)return[];let r=Zu(e);return t.filter(s=>typeof s=="string"&&s!==e&&!Xu.has(s)&&!s.startsWith("_")&&!s.startsWith("subflow:")).map(s=>({c:s,dist:Vu(e,s),prefix:Yu(e,s)})).filter(s=>s.dist<=r).sort((s,i)=>s.dist-i.dist||i.prefix-s.prefix||s.c.localeCompare(i.c)).slice(0,n).map(s=>s.c)}function Qu(e){return e.endsWith("/")?e.slice(0,-1):e}function Ta(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,xa.createRequire)(e)}function ep(){try{let t=Ta()("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}function Ra(e){let t=e.types,n=Array.isArray(t)?t.filter(i=>typeof i=="string"&&i.length>0):[],r="";if(typeof e.module=="string"&&e.module)r=e.module;else if(typeof e.name=="string"&&e.name)r=e.name;else if(typeof e.id=="string"){let i=String(e.id).split("/")[0];r=i&&i.length>0?i:String(e.id)}let o=typeof e.id=="string"&&e.id?e.id:n.length===1&&r?`${r}/${n[0]}`:r;if(!o||n.length===0)return null;let s={id:o,module:r.length>0?r:o,types:n};return typeof e.version=="string"&&(s.version=e.version),typeof e.enabled=="boolean"&&(s.enabled=e.enabled),typeof e.name=="string"&&(s.name=e.name),s}function Ls(e=Ju,t={}){let n=t.module??"node-red",r=t.version??"4.0.0",o=[...e],s=t.configs??{};return{listNodeSets(){return Promise.resolve([{id:n,module:n,version:r,types:o,enabled:!0,name:n}])},getNodeConfigHtml(i){if(s[i])return Promise.resolve(s[i]??null);let d=i.includes("/")?i.slice(i.lastIndexOf("/")+1):i;return s[d]?Promise.resolve(s[d]??null):Promise.resolve(null)}}}function Pa(e={}){if(ep())try{let n=Ta()("@node-red/runtime");if(n.nodes&&typeof n.nodes.getNodeList=="function")return new Es(n.nodes)}catch{}if(e.baseUrl)return new _s({baseUrl:e.baseUrl,...e.accessToken?{accessToken:e.accessToken}:{},...e.fetch?{fetch:e.fetch}:{}});throw new Error("No usable NodeTypesAdapter: runtime.nodes unavailable and no HTTP baseUrl")}async function Ns(e){let t=await e.listNodeSets(),n=new Map;for(let r of t)for(let o of r.types){if(n.has(o))continue;let s={type:o,module:r.module,setId:r.id};typeof r.enabled=="boolean"&&(s.enabled=r.enabled),r.version&&(s.version=r.version),n.set(o,s)}return{byType:n,allNames:[...n.keys()]}}function tp(e,t){let n=t.toLowerCase();return[e.type,e.module,e.category??"",e.description??""].join(`
|
|
4
|
-
`).toLowerCase().includes(n)}async function
|
|
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
|
|
6
|
-
`;break;case"r":r="\r";break;case"t":r=" ";break;case"b":r="\b";break;case"f":r="\f";break;case"v":r="\v";break;case"0":r="\0";break;case"\\":r="\\";break;case"'":r="'";break;case'"':r='"';break;case"/":r="/";break;default:r=n;break}return{char:r,next:t+1}}function dp(e){return e==="_DEFAULT_"?"":e}function io(e){let t=e.trimStart();if(t.startsWith("'")||t.startsWith('"')){let r=t[0],o=1,s="";for(;o<t.length;){let i=t[o];if(i==="\\"&&o+1<t.length){let d=ap(t,o+1);s+=d.char,o=d.next;continue}if(i===r)break;s+=i,o+=1}return dp(s)}if(t.startsWith("[")){let r=_a(t);if(!r)return;let o=r.slice(1,-1).trim();if(!o)return[];let s=[],i=0;for(;i<o.length;){for(;i<o.length&&/[\s,]/.test(o.charAt(i));)i+=1;if(i>=o.length)break;let d=o.slice(i),l=io(d);s.push(l);let c=Ps(d);i+=c>0?c:1}return s}if(t.startsWith("{")){let r=sr(t);if(!r)return;let o=r.slice(1,-1);if(!o.trim())return{};let s={},i=0;for(;i<o.length;){for(;i<o.length&&/[\s,]/.test(o.charAt(i));)i+=1;if(i>=o.length)break;let d;if(o[i]==="'"||o[i]==='"'){let c=io(o.slice(i));d=typeof c=="string"?c:"",i+=Ps(o.slice(i))}else{let c=/^[a-zA-Z_$][\w$]*/.exec(o.slice(i));if(!c)break;d=c[0],i+=d.length}for(;i<o.length&&/\s/.test(o.charAt(i));)i+=1;if(o[i]!==":")break;for(i+=1;i<o.length&&/\s/.test(o.charAt(i));)i+=1;let l=o.slice(i);s[d]=io(l),i+=Ps(l)}return s}let n=/^-?\d+(?:\.\d+)?/.exec(t);if(n)return Number(n[0]);if(t.startsWith("true"))return!0;if(t.startsWith("false"))return!1;if(t.startsWith("null"))return null}function Ps(e){let t=e.trimStart(),n=e.length-t.length;if(t.startsWith("'")||t.startsWith('"')){let o=t[0],s=1;for(;s<t.length;){if(t[s]==="\\"&&s+1<t.length){s+=2;continue}if(t[s]===o)return n+s+1;s+=1}return n+t.length}if(t.startsWith("[")){let o=_a(t);return n+(o?.length??1)}if(t.startsWith("{")){let o=sr(t);return n+(o?.length??1)}let r=/^(?:-?\d+(?:\.\d+)?|true|false|null)/.exec(t);return r?n+r[0].length:n+1}function _a(e){if(!e.startsWith("["))return null;let t=0,n=null,r=!1;for(let o=0;o<e.length;o++){let s=e[o];if(n){if(r){r=!1;continue}if(s==="\\"){r=!0;continue}s===n&&(n=null);continue}if(s==="'"||s==='"'){n=s;continue}if(s==="[")t+=1;else if(s==="]"&&(t-=1,t===0))return e.slice(0,o+1)}return null}function lp(e){return io(e)}function cp(e,t){for(let n of e)if(n.types.includes(t)){let r=n.id.includes("/")||n.types.length===1?n.id:`${n.module}/${t}`;return{set:n,setId:r}}return null}function Fs(e,t){let n=e.toLowerCase();return t.includes(e)?e:t.find(r=>r.toLowerCase()===n)}async function Bt(e,t,n={}){if(!t||typeof t!="string")throw new b("Missing node type","missing_node_type");let r=n.includeHelp!==!1;if(Cs(t)){let l={type:t,module:"node-red",category:"config",inputs:0,outputs:0,defaults:null};return r&&(l.help={summary:"Built-in structural Node-RED type (not a palette node)."}),l}let o=await e.listNodeSets(),s=o.flatMap(l=>l.types),i=cp(o,t);if(!i){let l=Fs(t,s);throw l&&l!==t?new b(`Node type '${t}' was not found \u2014 did you mean '${l}'? Types are case-sensitive.`,"node_type_not_found",{statusCode:404,details:{type:t,didYouMean:l,caseSensitive:!0,nearest:[l]}}):new b(`Node type '${t}' was not found`,"node_type_not_found",{statusCode:404,details:{type:t,nearest:Is(t,s,5)}})}let d={type:t,module:i.set.module,setId:i.setId,inputs:1,outputs:1,defaults:null};if(i.set.version&&(d.version=i.set.version),typeof i.set.enabled=="boolean"&&(d.enabled=i.set.enabled),typeof e.getNodeConfigHtml=="function")try{let l=await e.getNodeConfigHtml(i.setId)??await e.getNodeConfigHtml(`${i.set.module}/${t}`)??await e.getNodeConfigHtml(t);if(l){let c=op(l,t);c&&(c.category&&(d.category=c.category),c.color&&(d.color=c.color),d.inputs=c.inputs??d.inputs,d.outputs=c.outputs??d.outputs,c.defaults&&Object.keys(c.defaults).length>0?d.defaults=c.defaults:d.defaults=null,c.requiredConfigNodeTypes&&(d.requiredConfigNodeTypes=c.requiredConfigNodeTypes),r&&c.help&&(d.help=sp(c.help)))}}catch{}return d}async function Ea(e,t){let{allNames:n}=await Ns(e),r=new Set(n),o=new Set,s=a(l=>{let c=yt(l,"node.type");if(o.has(c)||Cs(c)){o.add(c);return}if(o.add(c),r.has(c))return;let u=Fs(c,n);throw u&&u!==c?new b(`Unknown node type '${c}' \u2014 did you mean '${u}'? Types are case-sensitive.`,"unknown_node_type",{details:{type:c,didYouMean:u,caseSensitive:!0,nearest:[u]}}):new b(`Unknown node type '${c}'. Call node-types / palette search; ask the user to install via Manage palette, then recheck.`,"unknown_node_type",{details:{type:c,nearest:Is(c,n,5)}})},"checkType"),i=a(l=>{if(!l||typeof l!="object"||Array.isArray(l))return;let c=l;typeof c.id=="string"&&c.id.length>0&&s(c.type)},"visitNodeOrConfig");a(l=>{if(!l||typeof l!="object"||Array.isArray(l))return;let c=l;if(c.type!==void 0&&s(c.type),Array.isArray(c.nodes))for(let u of c.nodes)i(u);if(Array.isArray(c.configs))for(let u of c.configs)i(u)},"visitTab")(t)}async function Ca(e,t){let n=yt(t,"node.type");if(Cs(n))return;let{allNames:r}=await Ns(e);if(r.includes(n))return;let o=Fs(n,r);throw o&&o!==n?new b(`Unknown node type '${n}' \u2014 did you mean '${o}'? Types are case-sensitive.`,"unknown_node_type",{details:{type:n,didYouMean:o,caseSensitive:!0,nearest:[o]}}):new b(`Unknown node type '${n}'. Call node-types / palette search; ask the user to install via Manage palette, then recheck.`,"unknown_node_type",{details:{type:n,nearest:Is(n,r,5)}})}async function up(e,t){let n=new Headers({Accept:"application/json"});t&&n.set("If-None-Match",t);let r=await fetch(e,{headers:n}),o=r.headers.get("etag")??void 0;if(r.status===304)return{status:304,...o?{etag:o}:{}};if(!r.ok)return{status:r.status,body:null,...o?{etag:o}:{}};let s=await r.json();return{status:r.status,body:s,...o?{etag:o}:{}}}async function pp(e,t){let n=Date.now();if(Ne&&n-Ne.fetchedAt<Ku)return Ne.body;let r=Ne?.etag,o=await e(t,r);if(o.status===304&&Ne)return Ne={...Ne,fetchedAt:n,...o.etag?{etag:o.etag}:{}},Ne.body;if(!o.body||!Array.isArray(o.body.modules)){if(Ne)return Ne.body;throw new b("Node-RED catalogue unavailable","catalogue_unavailable",{statusCode:502})}return Ne={fetchedAt:n,body:o.body,...o.etag?{etag:o.etag}:{}},o.body}async function lo(e,t,n={}){let r=t.trim();if(!r)throw new b("q query parameter is required","missing_query");let o=n.limit??zu,s=n.catalogueUrl??Gu,i=n.catalogueFetcher??up,d=await pp(i,s),l=Array.isArray(d.modules)?d.modules:[],c=await e.listNodeSets(),u=new Set(c.flatMap(g=>{let y=[g.module,g.id];return g.id.includes("/")&&y.push(g.id.split("/")[0]??g.module),y.map(A=>A.toLowerCase())})),p=r.toLowerCase(),f=[];for(let g of l){let y=g.id.toLowerCase(),A=(g.keywords??[]).map(D=>String(D).toLowerCase()),x=(g.description??"").toLowerCase(),$=fp(p,y,A,x);if($<=0)continue;let L={module:g.id,installed:u.has(y)};typeof g.description=="string"&&(L.description=g.description),typeof g.version=="string"&&(L.version=g.version),typeof g.updated_at=="string"&&(L.updated=g.updated_at),typeof g.downloads=="number"&&(L.downloads=g.downloads),typeof g.url=="string"&&(L.link=g.url),f.push({item:L,score:$})}f.sort((g,y)=>y.score-g.score||(y.item.downloads??0)-(g.item.downloads??0)||g.item.module.localeCompare(y.item.module));let h=f.length,m=h>o;return{q:r,modules:f.slice(0,o).map(g=>g.item),total:h,truncated:m,hint:Wu}}function fp(e,t,n,r){if(!e)return 0;if(t===e||t===`node-red-contrib-${e}`||t===`node-red-node-${e}`)return 1e4;let o=0;t.startsWith(`node-red-contrib-${e}`)||t.startsWith(`node-red-node-${e}`)?o+=500:t.startsWith(e)?o+=80:t.includes(e)&&(o+=60);for(let s of n)s===e?o+=50:s.startsWith(e)||e.startsWith(s)?o+=35:s.includes(e)&&(o+=25);return r.includes(e)&&(o+=10),o}var xa,Bu,qu,zu,Wu,Gu,Ku,Ju,Xu,_s,ba,Es,Ne,qt=q(()=>{"use strict";xa=require("module");ie();Bu=new Set(["tab","subflow","group","junction"]),qu=100,zu=30,Wu="Recommend only \u2014 ask the user to install via Manage palette, then recheck node-types. This API never installs. Core Node-RED nodes (module node-red) are not listed in the public catalogue; use node-types for installed core types.",Gu="https://catalogue.nodered.org/catalogue.json",Ku=3600*1e3,Ju=["inject","debug","function","template","comment","change","switch","delay","trigger","link in","link out","link call","mqtt in","mqtt out","http in","http response","http request","websocket in","websocket out","tcp in","tcp out","udp in","udp out","catch","status","complete","join","split","sort","batch","csv","html","json","xml","yaml","file","file in","watch","exec","range","rbe"];a(Cs,"isStructuralNodeType");a(yt,"requireNodeTypeName");a(Vu,"editDistance");a(Yu,"sharedPrefixLength");Xu=new Set(["unknown","tab","subflow","group"]);a(Zu,"nearestDistanceCutoff");a(Is,"nearestNodeTypes");a(Qu,"trimTrailingSlash");a(Ta,"createPluginRequire");a(ep,"isRuntimeSettingsAvailable");a(Ra,"normalizeNodeSet");a(Ls,"createStaticNodeTypesAdapter");_s=class{static{a(this,"HttpNodeTypesAdapter")}baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=Qu(t.baseUrl),this.accessToken=t.accessToken,this.fetchImpl=t.fetch??globalThis.fetch.bind(globalThis)}headers(t){let n=new Headers;return n.set("Accept",t),this.accessToken&&n.set("Authorization",`Bearer ${this.accessToken}`),n}async listNodeSets(){let t=await this.fetchImpl(`${this.baseUrl}/nodes`,{headers:this.headers("application/json")});if(!t.ok)throw new Error(`Admin API GET /nodes failed (${t.status})`);let n=await t.json();if(!Array.isArray(n))throw new Error("Admin API GET /nodes returned non-array JSON");let r=[];for(let o of n){if(!o||typeof o!="object"||Array.isArray(o))continue;let s=Ra(o);s&&r.push(s)}return r}async getNodeConfigHtml(t,n){let r=t.split("/").map(i=>encodeURIComponent(i)).join("/"),o=n?`?lang=${encodeURIComponent(n)}`:"",s=await this.fetchImpl(`${this.baseUrl}/nodes/${r}${o}`,{headers:this.headers("text/html")});return s.status===404||!s.ok?null:s.text()}},ba={user:null,req:null},Es=class{constructor(t){this.nodes=t}static{a(this,"RedNodeTypesAdapter")}async listNodeSets(){if(typeof this.nodes.getNodeList!="function")return[];let t,n=this.nodes.getNodeList;try{t=await Promise.resolve(n(ba))}catch{try{t=await Promise.resolve(n())}catch{return[]}}if(!Array.isArray(t))return[];let r=[];for(let o of t){if(!o||typeof o!="object"||Array.isArray(o))continue;let s=Ra(o);s&&r.push(s)}return r}async getNodeConfigHtml(t,n){if(typeof this.nodes.getNodeConfig!="function")return null;try{let r=await Promise.resolve(this.nodes.getNodeConfig({id:t,lang:n??"en-US",...ba}));if(typeof r=="string"&&r.length>0)return r}catch{}try{let r=await Promise.resolve(this.nodes.getNodeConfig(t,n));return typeof r=="string"&&r.length>0?r:null}catch{return null}}};a(Pa,"createNodeTypesAdapter");a(Ns,"flattenInstalledTypes");a(tp,"matchesQuery");a(ao,"listNodeTypes");a(np,"extractHelpScriptBody");a(va,"removeHtmlBlocks");a(rp,"stripHelpText");a(op,"parseRegisterTypeFromHtml");a(ka,"parseRegisterTypeAt");a(sp,"splitHelpSections");a(sr,"extractBalancedObject");a(Aa,"matchStringProp");a(Sa,"matchNumberProp");a(ip,"parseDefaultsBlock");a(ap,"unescapeJsStringEscape");a(dp,"normalizeDefaultValue");a(io,"parseJsLiteral");a(Ps,"measureJsLiteralLength");a(_a,"extractBalancedArray");a(lp,"parseLooseValue");a(cp,"findSetForType");a(Fs,"findCaseInsensitiveType");a(Bt,"getNodeTypeDetail");a(Ea,"assertKnownNodeTypes");a(Ca,"assertKnownNodeType");Ne=null;a(up,"defaultCatalogueFetcher");a(pp,"loadCatalogue");a(lo,"searchPalette");a(fp,"paletteMatchScore")});function at(e){if(e.length===0)return;let t=e[0];if(!t)return;let n=e.map(s=>{let i={...s};return delete i.statusCode,i});if(e.length>1)throw new b(`${e.length} validation errors`,"validation_failed",{statusCode:400,details:{errors:n,errorCount:e.length}});let r={errors:n};for(let[s,i]of Object.entries(t))s==="code"||s==="error"||s==="errors"||s==="statusCode"||i!==void 0&&(r[s]=i);let o=typeof t.statusCode=="number"?t.statusCode:400;throw new b(t.error,t.code,{statusCode:o,details:r})}function Os(e,t="payload.flow"){let n=new Map;for(let o of e)for(let s of o){if(!s||typeof s!="object"||Array.isArray(s))continue;let i=s.id;typeof i=="string"&&i.length>0&&n.set(i,(n.get(i)??0)+1)}let r=[];for(let[o,s]of n)s>1&&r.push({code:"duplicate_node_id",error:`Node id '${o}' appears ${s} times in ${t}; node ids must be unique`,nodeId:o,count:s});return r}function hp(e){let t=typeof e.label=="string"&&e.label?` '${e.label}'`:"";return e.type==="tab"?`tab${t||` '${e.id}'`}`:e.type==="subflow"?`subflow${t||` '${e.id}'`}`:typeof e.z=="string"&&e.z?`${e.type} node on '${e.z}'`:`global ${e.type} config node`}function La(e,t,n){let r=new Map;for(let i of n)!i||typeof i.id!="string"||i.id===t||i.z===t||r.set(i.id,i);let o=[],s=new Set;for(let i of[e.nodes,e.configs])if(Array.isArray(i))for(let d of i){let l=d&&typeof d=="object"?d.id:void 0;if(typeof l!="string"||!l||s.has(l))continue;if(s.add(l),l===t){o.push({code:"duplicate_node_id",error:`Node id '${l}' equals the tab id; node ids must be unique across all flows`,nodeId:l});continue}let c=r.get(l);c&&o.push({code:"duplicate_node_id",error:`Node id '${l}' is already used by ${hp(c)}; node ids must be unique across all flows`,nodeId:l})}return o}function Ia(e){let t=new Map;for(let n of[e.nodes,e.configs])if(Array.isArray(n))for(let r of n){if(!r||typeof r!="object"||Array.isArray(r))continue;let o=r;typeof o.id=="string"&&t.set(o.id,o)}return t}function gp(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]);for(let r of n)if(!mp.has(r)&&JSON.stringify(e[r])!==JSON.stringify(t[r]))return!0;return!1}function yp(e,t){let n=Ia(t);if(!e)return new Set(n.keys());let r=Ia(e),o=new Set;for(let[s,i]of n){let d=r.get(s);(!d||gp(d,i))&&o.add(s)}return o}function Na(e,t){let n=[],r=e;typeof r.id!="string"||r.id.length===0?n.push({code:"missing_flow_id",error:"payload.flow.id is required and must match payload.flowId"}):r.id!==t&&n.push({code:"invalid_flow_id",error:`payload.flow.id ('${r.id}') must match payload.flowId ('${t}')`}),"nodes"in r?Array.isArray(r.nodes)||n.push({code:"invalid_nodes",error:"payload.flow.nodes must be an array"}):n.push({code:"invalid_nodes",error:"payload.flow.nodes must be an array (use [] to clear the tab intentionally)"}),"configs"in r&&r.configs!==void 0&&!Array.isArray(r.configs)&&n.push({code:"invalid_nodes",error:"payload.flow.configs must be an array"});let o=[];Array.isArray(r.nodes)&&o.push({key:"nodes",list:r.nodes}),Array.isArray(r.configs)&&o.push({key:"configs",list:r.configs}),n.push(...Os(o.map(s=>s.list)));for(let{key:s,list:i}of o)for(let d=0;d<i.length;d+=1){let l=i[d];if(!l||typeof l!="object"||Array.isArray(l)){n.push({code:"invalid_node",error:`payload.flow.${s}[${d}] must be an object`});continue}let c=l;(typeof c.id!="string"||c.id.length===0)&&n.push({code:"missing_node_id",error:`payload.flow.${s}[${d}] needs a non-empty string id`});try{yt(c.type,"node.type")}catch(u){u instanceof b&&n.push({code:u.code,error:u.message,...typeof c.id=="string"?{nodeId:c.id}:{}})}"wires"in c&&wn(c.wires,n,typeof c.id=="string"?c.id:void 0)}return n}function wn(e,t,n){if(!Array.isArray(e))return t.push({code:"invalid_wires",error:"wires must be an array of output ports (each a list of node ids)",...n?{nodeId:n}:{}}),!1;for(let r of e){if(!Array.isArray(r))return t.push({code:"invalid_wires",error:"wires must be an array of output ports (each a list of node ids)",...n?{nodeId:n}:{}}),!1;for(let o of r)if(typeof o!="string"||o.length===0)return t.push({code:"invalid_wires",error:"wires targets must be non-empty node id strings",...n?{nodeId:n}:{}}),!1}return!0}async function wp(e,t){if(typeof e.outputs=="number"&&Number.isFinite(e.outputs)&&e.outputs>=0)return Math.trunc(e.outputs);let n=typeof e.type=="string"?e.type:"";if(!(!t||!n))try{return(await Bt(t,n,{includeHelp:!1})).outputs}catch{return}}function bp(e,t,n){let r=typeof e.type=="string"?e.type:"node";if(!(n.length<=t))return t===0?{code:"invalid_wires",error:`${r} has 0 outputs; wires must be []`,nodeId:e.id,type:r}:{code:"invalid_wires",error:`${r} has ${t} outputs; wires has ${n.length} port arrays (max ${t})`,nodeId:e.id,type:r}}function vp(e){return Array.isArray(e)&&e.some(t=>typeof t=="string"&&t.length>0)}function kp(e,t){return t<=0?!1:!("wires"in e)||e.wires===void 0?!0:Array.isArray(e.wires)?!e.wires.some(vp):!1}async function $s(e,t,n={}){let r=[],o=[],s=new Set(e.map(d=>d.id)),i=n.softWarningNodeIds;for(let d of e){let l=await wp(d,t),c=typeof d.type=="string"?d.type:"node",u=!i||i.has(d.id);if("wires"in d&&d.wires!==void 0){if(!wn(d.wires,r,d.id))continue;let p=d.wires;if(l!==void 0){let f=bp(d,l,p);f?(f.cause="excess_ports",r.push(f)):u&&p.length<l&&o.push({code:"incomplete_wires",error:`${c} '${d.id}' declares ${l} outputs but wires has only ${p.length} port(s)`,nodeId:d.id,type:c})}for(let f of p)for(let h of f){let m=String(h);s.has(m)||r.push({code:"invalid_wires",error:`Wire target '${m}' on node '${d.id}' was not found in this flow (add the node in the same patch or use an existing id)`,nodeId:d.id,cause:"missing_target",targetId:m})}}u&&l!==void 0&&kp(d,l)&&o.push({code:"unwired_node",error:`Node '${d.id}' (${c}) has ${l} output(s) but is not wired to anything yet`,nodeId:d.id,type:c})}return{errors:r,warnings:o}}function Ms(e,t){let n=[];for(let r of e)r.z===void 0||r.z===""||typeof r.z=="string"&&r.z!==t&&n.push({code:"z_mismatch",error:`Node '${r.id}' has z='${r.z}' but flow id is '${t}'`,nodeId:r.id});return n}function Ap(e,t){return e.filter(n=>n.type===t).map(n=>n.id)}async function Ds(e,t,n){let r=[],o=[...t];for(let s of e){let i;try{i=yt(s.type,"node.type")}catch{continue}let d;try{d=await Bt(n,i,{includeHelp:!1})}catch{continue}let l=d.defaults;if(!l||typeof l!="object")continue;let c=new Set(d.requiredConfigNodeTypes??[]);for(let[u,p]of Object.entries(l)){if(!p||typeof p!="object"||Array.isArray(p))continue;let f=p.type;if(typeof f!="string"||!f)continue;let m=p.required===!0||c.has(f),g=s[u],y=Ap(o,f);if(typeof g!="string"||g.length===0){m&&r.push({code:"missing_config_node",error:`Node '${s.id}' (${i}) has no ${f} on '${u}' yet \u2014 configure before deploy if needed`,nodeId:s.id,type:i,property:u,configType:f,candidates:y});continue}o.find(x=>x.id===g)?.type!==f&&r.push({code:"missing_config_node",error:`Node '${s.id}' property '${u}' does not resolve to a ${f} config yet`,nodeId:s.id,type:i,property:u,configType:f,candidates:y})}}return r}async function Fa(e,t={}){let n=t.flowId??(typeof e.id=="string"?e.id:""),r=Array.isArray(e.nodes)?e.nodes:[],o=Array.isArray(e.configs)?e.configs:[],s=[],i=[];n&&s.push(...Ms(r,n));let d=t.baseFlow?yp(t.baseFlow,e):void 0,l=await $s(r,t.nodeTypes,{...d?{softWarningNodeIds:d}:{}});if(s.push(...l.errors),i.push(...l.warnings),t.nodeTypes){let c=await Ds(r,o,t.nodeTypes);d?i.push(...c.filter(u=>!u.nodeId||d.has(u.nodeId))):i.push(...c)}return{errors:s,warnings:i}}var mp,Hs=q(()=>{"use strict";ie();qt();a(at,"throwValidationIssues");a(Os,"collectDuplicateNodeIdIssues");a(hp,"describeLiveNode");a(La,"collectCrossFlowDuplicateIssues");mp=new Set(["x","y","w","h","g","id","z"]);a(Ia,"collectNodesMap");a(gp,"nodeContentChanged");a(yp,"changedNodeIds");a(Na,"collectFullFlowShapeIssues");a(wn,"assertValidWiresShape");a(wp,"resolveNodeOutputCount");a(bp,"wireCountIssue");a(vp,"portHasTargets");a(kp,"nodeIsUnwired");a($s,"collectWireValidation");a(Ms,"collectZMismatchIssues");a(Ap,"configCandidates");a(Ds,"collectConfigWarnings");a(Fa,"validateFlowTab")});function wt(e,t="node"){return typeof e=="string"&&e.trim()?e.trim():t}function co(e){let t=new Map;for(let n of e)n.type==="tab"&&typeof n.id=="string"&&typeof n.label=="string"&&n.label.trim()&&t.set(n.id,n.label.trim());return t}function $a(e,t){e[Oa]=t}function Tp(e){if(!e)return;let t=e[Oa];return t instanceof Map?t:void 0}function Rp(e){return typeof e!="string"||!e.trim()?new Set:new Set(e.split(",").map(t=>t.trim()).filter(Boolean))}function Pp(e){if(e===!0||e===1)return!0;if(typeof e=="string"){let t=e.trim().toLowerCase();return t==="1"||t==="true"||t==="yes"}return!1}function ge(e){let t=Rp(e.include);return Pp(e.include_payload)&&(t.add("payload"),t.add("baseFlow")),t}function V(e,t){let n={req:e};t!==void 0&&(n.httpAdminRoot=t);let r=Tp(e);return r&&(n.liveFlowLabels=r),n}function bn(e){return typeof e.name=="string"&&e.name.trim()?e.name.trim():typeof e.label=="string"&&e.label.trim()?e.label.trim():""}function ir(e){return`"${e.replace(/"/g,"'")}"`}function vn(e){let t=new Map;for(let n of e.nodes??[])t.set(n.id,n);for(let n of e.configs??[])t.set(n.id,n);return t}function _p(e,t){let n=new Set(["x","y","w","h","g","id","z"]),r=new Set([...Object.keys(e),...Object.keys(t)]);for(let o of r)if(!n.has(o)&&JSON.stringify(e[o])!==JSON.stringify(t[o]))return!0;return!1}function Ep(e,t){let n=bn(e),r=bn(t);if(!n||!r||n===r)return!1;let o=new Set(["x","y","w","h","g","id","z","name","label"]),s=new Set([...Object.keys(e),...Object.keys(t)]);for(let i of s)if(!o.has(i)&&JSON.stringify(e[i])!==JSON.stringify(t[i]))return!1;return!0}function js(e,t){let n=[],r=[],o=[];for(let[s,i]of t)e.has(s)||n.push(i);for(let[s,i]of e)t.has(s)||r.push(i);for(let[s,i]of t){let d=e.get(s);d&&_p(d,i)&&o.push({before:d,after:i})}return{added:n,removed:r,modified:o}}function uo(e){return e===1?"node":"nodes"}function Cp(e){return e.length===1&&e[0]?`adds 1 ${wt(e[0].type)}`:`adds ${e.length} ${uo(e.length)}`}function Ip(e){return`deletes ${e} ${uo(e)}`}function Lp(e){return`edits ${e} ${uo(e)}`}function Np(e){let t=[];e.removed>0&&t.push(Ip(e.removed));let n=e.added??[],r=e.addedCount??n.length;r>0&&t.push(n.length===r?Cp(n):`adds ${r} ${uo(r)}`),(e.modified??0)>0&&t.push(Lp(e.modified??0));let o=t.length?t.join(", "):"replaces nodes",s=e.flowLabel?.trim();return s?`Replace ${s}: ${o}`:`Replace flow: ${o}`}function Fp(e){let{added:t,removed:n,modified:r}=e;if(t.length+n.length+r.length!==1)return;if(t[0]){let u=t[0],p=wt(u.type),f=bn(u);return f?`Add ${p} node ${ir(f)}`:`Add ${p} node`}if(n[0]){let u=n[0],p=wt(u.type),f=bn(u);return f?`Remove ${p} node ${ir(f)}`:`Remove ${p} node`}let s=r[0];if(!s)return;let i=wt(s.after.type,wt(s.before.type)),d=bn(s.before),l=bn(s.after);if(Ep(s.before,s.after)&&d&&l)return`Rename ${i} node ${ir(d)} \u2192 ${ir(l)}`;let c=l||d;return c?`Edit ${i} ${ir(c)}`:`Edit ${i}`}function Us(e,t){return typeof e?.label=="string"&&e.label.trim()?e.label.trim():typeof t?.label=="string"&&t.label.trim()?t.label.trim():""}function po(e,t){if(typeof e.flowLabel=="string"&&e.flowLabel.trim())return e.flowLabel.trim();let n=Us(e.payload?.flow,e.baseFlow);if(n)return n;let r=e.payload?.flowId;return r&&t?.has(r)?t.get(r)??"":""}function Ma(e,t){let n=t.trim(),r=e.trim();if(!n||!r||r.includes(n))return r;let o=r.lastIndexOf(" in ");if(o>=0){let s=r.slice(o+4);if(s.length>0&&n.startsWith(s))return r}return`${r} in ${n}`}function Bs(e,t){let n=js(vn(e??{}),vn(t)),r=n.added.length+n.removed.length+n.modified.length,o=Us(t,e);if(r===0)return o?`Update ${o}`:"Update flow";if(n.removed.length>0)return Np({...o?{flowLabel:o}:{},removed:n.removed.length,added:n.added,modified:n.modified.length});if(r>xp)return o?`${r} changes in ${o}`:`${r} changes`;let s=Fp(n)??(o?`Update ${o}`:"Update flow");return Ma(s,o)}function Op(e,t){let n=(e??"").trim();return!!(!n||n.includes("[object Object]")||/^Update(\s|$)/i.test(n)||t>0&&!/^Replace\b/i.test(n))}function qs(e,t){return`${e} \xB7 ${Mp(t)}`}function $p(e){let t=e>=0?"+":"-",n=Math.abs(e),r=String(Math.floor(n/60)).padStart(2,"0"),o=String(n%60).padStart(2,"0");return`${t}${r}:${o}`}function Mp(e,t=new Date(e)){let n=Number.isNaN(t.getTime())?new Date(e):t;if(Number.isNaN(n.getTime()))return e.slice(0,16);let r=n.getDate(),s=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"][n.getMonth()]??"Jan",i=String(n.getHours()).padStart(2,"0"),d=String(n.getMinutes()).padStart(2,"0"),l=$p(-n.getTimezoneOffset());return`${r} ${s} ${i}:${d} ${l}`}function ar(e){if(typeof e!="string")return;let t=e.replace(/\0/g,"").replace(/[\u0001-\u001F\u007F]/g," ").replace(/\s+/g," ").trim().slice(0,Sp).trim();return t.length>0?t:void 0}function Dp(e,t){let n=Ae(t);return`${n==="/"?"/ai-access":`${n}/ai-access`}#proposal=${encodeURIComponent(e)}`}function zs(e,t={}){let n=po(e,t.liveFlowLabels),r=ar(e.title),o=Bs(e.baseFlow,n&&!Us(e.payload.flow,e.baseFlow)?{...e.payload.flow,label:n}:e.payload.flow),s=js(vn(e.baseFlow??{}),vn(e.payload.flow)),i=Op(r,s.removed.length)?o:r??o;return Ma(i,n)}function Hp(e){return typeof e.storedTitle=="string"&&e.storedTitle?e.storedTitle:ar(e.title)}function pe(e,t={}){let n=zs(e,t),r=po(e,t.liveFlowLabels),o=Hp(e)??n,s=Dp(e.id,t.httpAdminRoot),i=t.req?rr(t.req,s):s;return{...e,title:n,storedTitle:o,flowLabel:r,displayName:qs(n,e.createdAt),reviewUrl:i,reviewPath:s}}function jp(e){let t=js(vn(e.baseFlow??{}),vn(e.payload.flow)),n=[];return t.added.length>0&&n.push(t.added.length===1?"Adds 1 node":`Adds ${t.added.length} nodes`),t.removed.length>0&&n.push(t.removed.length===1?"Deletes 1 node":`Deletes ${t.removed.length} nodes`),t.modified.length>0&&n.push(t.modified.length===1?"Edits 1 node":`Edits ${t.modified.length} nodes`),n.length===0?"No node changes":n.map((r,o)=>o===0?r:r.charAt(0).toLowerCase()+r.slice(1)).join(", ")}function Up(e){if(e.kind==="oauth")return{kind:"oauth",clientId:e.clientId,user:e.user};let t={kind:e.kind,linkId:e.linkId};return typeof e.label=="string"&&e.label.length>0&&(t.label=e.label),t}function fe(e,t={}){let n=pe(e,t),r=t.include??new Set,o={id:n.id,status:n.status,type:n.type,title:n.title,storedTitle:n.storedTitle,displayName:n.displayName,flowLabel:n.flowLabel,reviewUrl:n.reviewUrl,reviewPath:n.reviewPath,createdAt:n.createdAt,updatedAt:n.updatedAt,expectedRev:n.expectedRev,createdBy:Up(n.createdBy),changeSummary:jp(e)};return r.has("payload")&&(o.payload=n.payload),r.has("baseFlow")&&n.baseFlow!==void 0&&(o.baseFlow=n.baseFlow),n.warnings&&n.warnings.length>0&&(o.warnings=n.warnings),typeof e.payload.flowId=="string"&&(o.flowId=e.payload.flowId),e.restoredSecrets&&e.restoredSecrets.length>0&&(o.restoredSecrets=e.restoredSecrets),n.deployPending&&(o.deployPending=n.deployPending),n.rebase&&(o.rebase=n.rebase),n.baseRevStale!==void 0&&(o.baseRevStale=n.baseRevStale),n.decidedBy!==void 0&&(o.decidedBy=n.decidedBy),n.reason!==void 0&&(o.reason=n.reason),n.resultRev!==void 0&&(o.resultRev=n.resultRev),o}function Da(e,t={}){return e.map(n=>fe(n,t))}var Sp,xp,Oa,Fe=q(()=>{"use strict";ve();Be();Sp=80,xp=1;a(wt,"nodeTypeText");Oa=Symbol.for("ai-access.liveFlowLabels");a(co,"liveFlowLabelsFromFlows");a($a,"setRequestFlowLabels");a(Tp,"getRequestFlowLabels");a(Rp,"parseIncludeQuery");a(Pp,"isTruthyFlag");a(ge,"resolveIncludeFromQuery");a(V,"presentOptionsFor");a(bn,"nodeLabel");a(ir,"quoteName");a(vn,"collectNodes");a(_p,"nodeContentChanged");a(Ep,"isRenameOnly");a(js,"classifyFlowChanges");a(uo,"pluralNodes");a(Cp,"describeAddedClause");a(Ip,"describeRemovedClause");a(Lp,"describeModifiedClause");a(Np,"buildReplaceFlowTitle");a(Fp,"describeOneChange");a(Us,"flowLabelHint");a(po,"resolveFlowLabel");a(Ma,"ensureTitleIncludesFlowName");a(Bs,"autoGenerateProposalTitle");a(Op,"shouldRefreshDisplayTitle");a(qs,"buildProposalDisplayName");a($p,"formatUtcOffset");a(Mp,"formatProposalSubmittedAt");a(ar,"sanitizeProposalTitle");a(Dp,"buildProposalReviewPath");a(zs,"resolveProposalTitle");a(Hp,"storedTitleOf");a(pe,"presentProposal");a(jp,"buildChangeSummary");a(Up,"presentCreatedByForApi");a(fe,"presentProposalForApi");a(Da,"presentProposalsForApi")});function Ha(e){let t=new Map;for(let n of e.nodes??[])t.set(n.id,n);for(let n of e.configs??[])t.set(n.id,n);return t}function qp(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let o of n)Bp.has(o)||JSON.stringify(e[o])!==JSON.stringify(t[o])&&r.push(o);return r.sort()}function ja(e){let t=Ha(e.baseFlow??{}),n=Ha(e.payload.flow),r=new Map;for(let[o,s]of n){let i=t.get(o);if(!i){r.set(o,new Set(["*"]));continue}let d=qp(i,s);d.length>0&&r.set(o,new Set(d))}for(let[o]of t)n.has(o)||r.set(o,new Set(["*"]));return r}function zp(e,t){let n=[],r=[];for(let[o,s]of e){let i=t.get(o);if(!i)continue;n.push(o);let d=[];for(let c of s){if(c==="*"){d.push(...[...i].filter(u=>u!=="*")),i.has("*")&&d.push("*");continue}(i.has(c)||i.has("*"))&&d.push(c)}let l=[...new Set(d)].sort();l.length>0&&r.push({nodeId:o,fields:l})}return n.sort(),{nodeIds:n,fields:r}}function Ve(e,t,n){let r=ja(t),o=[];for(let s of e.list()){if(s.id===t.id||!U(s.status)||s.payload.flowId!==t.payload.flowId)continue;let i=zs(s),d=n?Ut(s.createdBy)===n:Ut(s.createdBy)===Ut(t.createdBy),l=zp(r,ja(s));if(l.nodeIds.length===0)continue;let c={id:s.id,title:i,displayName:qs(i,s.createdAt),mine:d};l.nodeIds.length>0&&(c.overlappingNodeIds=l.nodeIds),l.fields.length>0&&(c.overlappingFields=l.fields),o.push(c)}return{conflictingProposals:o}}function Ye(e){let t={};return e.conflictingProposals.length>0&&(t.conflictingProposals=e.conflictingProposals,t.warning="Other pending proposals touch the same flow \u2014 approve order may conflict (like a git merge)."),t}function ue(e){return e>0?{autoClosedCount:e}:{}}var Bp,zt=q(()=>{"use strict";Je();Fe();Bp=new Set(["x","y","w","h","g","id","z"]);a(Ha,"collectNodes");a(qp,"changedFieldKeys");a(ja,"computeProposalTouches");a(zp,"overlapBetween");a(Ve,"findConflictingProposals");a(Ye,"conflictWarningFields");a(ue,"autoClosedFields")});function kn(e){let t=e.join("\0"),n=fo.get(t);if(n)return n;let r=[...new Set(e.map(d=>jr(d).join("")).filter(Boolean))],o=new Map,s={patterns:[...e],matches(d){if(typeof d!="string"||d.length===0||r.length===0)return!1;let l=o.get(d);if(l!==void 0)return l;let c=i(d);return o.size>4096&&o.clear(),o.set(d,c),c}};function i(d){{let l=jr(d),c=l[l.length-1];if(!c||Wp.has(c))return!1;for(let u of r){let p=`${u}S`;for(let f=0;f<l.length;f++){let h="";for(let g=f;g<l.length;g++){if(h+=l[g],h===u||h===p)return!0;if(h.length>p.length)break}let m=l[f]??"";if(u.length>=6&&(m.endsWith(u)||m.endsWith(p)))return!0}}return!1}}return a(i,"matchUncached"),fo.size>32&&fo.clear(),fo.set(t,s),s}function dt(){return ta()?.getEffective()?.secretNamePatterns??Ft}function cr(e){let t=e.trim();return!(!t||t===J||Gp.has(t.toLowerCase())||/^\$\{[A-Za-z0-9_]+\}$/.test(t))}function Jp(e){let t=qa.get(e);return t||(t=Vp(e),qa.set(e,t)),t}function Vp(e){return[{hints:["Buffer.from","btoa"],re:/\b(?:Buffer\.from|btoa)\([ \t]{0,8}(?<q>["'`])[^"'`:\s]{1,256}:(?<s>[^"'`\s]{1,1024})\k<q>/dg,accept:a(t=>!t.includes("${"),"accept")},{hints:["PRIVATE KEY-----"],re:/-----BEGIN ((?:[A-Z0-9]{1,16} ){0,4})PRIVATE KEY-----(?<s>[^-]{1,16384})-----END \1PRIVATE KEY-----/dg},{hints:["AKIA","ASIA","ABIA","ACCA"],re:/(?<![A-Za-z0-9])(?<s>(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16})(?![A-Za-z0-9])/dg},{hints:["ghp_","gho_","ghu_","ghs_","ghr_"],re:/(?<![A-Za-z0-9_])(?<s>gh[pousr]_[A-Za-z0-9]{36,255})(?![A-Za-z0-9_])/dg},{hints:["github_pat_"],re:/(?<![A-Za-z0-9_])(?<s>github_pat_[A-Za-z0-9_]{22,255})(?![A-Za-z0-9_])/dg},{hints:["xox"],re:/(?<![A-Za-z0-9_-])(?<s>xox[abposr]-[A-Za-z0-9-]{10,512})/dg},{hints:["hooks.slack.com"],re:/hooks\.slack\.com\/services\/(?<s>[A-Za-z0-9_/]{16,512})/dg},{hints:["sk-"],re:/(?<![A-Za-z0-9_-])(?<s>sk-[A-Za-z0-9_-]{20,512})/dg},{hints:["k_live_","k_test_"],re:/(?<![A-Za-z0-9_])(?<s>[sr]k_(?:live|test)_[A-Za-z0-9]{16,512})/dg},{hints:["AIza"],re:/(?<![A-Za-z0-9_-])(?<s>AIza[0-9A-Za-z_-]{35})(?![A-Za-z0-9_-])/dg},{hints:["eyJ"],re:/(?<![A-Za-z0-9_-])(?<s>eyJ[A-Za-z0-9_-]{5,8192}\.[A-Za-z0-9_-]{5,8192}\.[A-Za-z0-9_-]{5,8192})/dg},{hints:["earer"],re:/\b[Bb]earer[ \t]{1,8}(?<s>(?=[A-Za-z0-9\-._~+/]{0,4096}[0-9\-._~+/])[A-Za-z0-9\-._~+/]{16,4096}={0,3})/dg},{hints:["asic"],re:/\b[Bb]asic[ \t]{1,8}(?<s>(?=[A-Za-z0-9+/]{0,4096}[0-9+/])[A-Za-z0-9+/]{8,4096}={0,2})(?![A-Za-z0-9+/])/dg},{hints:["@"],re:/\b[A-Za-z][A-Za-z0-9+.-]{1,20}:\/\/[^\s:@/'"`<>]{1,256}:(?<s>[^\s@/'"`<>]{1,1024})@/dg,accept:a(t=>!/^(?:\$\{|\{\{|<|%)/.test(t)&&t!==J,"accept")},{hints:["?","&"],re:/[?&](?<k>[A-Za-z_][\w.-]{0,63})=(?<s>[^&#\s"'`<>]{1,2048})/dg,accept:a((t,n)=>e.matches(n.groups?.k??"")&&cr(ef(t)),"accept")},{hints:[":","="],re:/(?<![\w$-])(?<q>["'`]?)(?<k>[A-Za-z_$][\w$-]{0,63})\k<q>\]?[ \t]{0,8}(?::|=(?![=>]))[ \t]{0,8}(?<vq>["'`])(?<s>(?:\\.|(?!\k<vq>)[^\\\r\n]){1,1024})\k<vq>/dg,accept:a((t,n)=>{let r=n.groups?.k??"";return!e.matches(r)||n.groups?.vq==="`"&&t.includes("${")||t.length<3||/\s/.test(t)?!1:cr(t)},"accept")}]}function Xp(e){let t=new Map;for(let r of e)t.set(r,(t.get(r)??0)+1);let n=0;for(let r of t.values()){let o=r/e.length;n-=o*Math.log2(o)}return n}function Zp(e){return/[^A-Za-z]/.test(e)||e.length>=16?!0:[/[a-z]/,/[A-Z]/,/[0-9]/,/[^A-Za-z0-9]/].filter(n=>n.test(e)).length>=3||Xp(e)>=3}function Qp(e){let t=e.patterns.filter(r=>Yp.includes(r.toUpperCase())),n=kn(t);return{hints:[":","="],re:/(?<![\w$.-])(?<k>[A-Za-z_][\w-]{0,63})[ \t]{0,8}(?::(?!\/\/)|=)[ \t]{0,8}(?<s>[^\s;,'"`&<>(){}[\]]{3,1024})/dg,accept:a((r,o)=>e.matches(o.groups?.k??"")&&cr(r)&&!r.startsWith("$")&&!r.startsWith("{{")&&(n.matches(o.groups?.k??"")||Zp(r))&&!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/.test(r)&&o.input.charAt(o.indices?.groups?.s?.[1]??0)!=="(","accept")}}function ef(e){try{return decodeURIComponent(e)}catch{return e}}function tf(e,t,n){let r=e.indexOf("=",t);if(r<=t||r>=n||r-t>48)return;let o=e.slice(t,r),s=Math.max(o.lastIndexOf('"'),o.lastIndexOf("'"),o.lastIndexOf("`")),i=o.slice(s+1).trim();if(!/^[A-Za-z][\w -]{0,40}$/.test(i))return;let d=r+1;for(;d<n&&(e[d]===" "||e[d]===" ");)d++;let l=d;for(;l<n&&!"\"'`\r".includes(e[l]??"");)l++;for(;l>d&&(e[l-1]===" "||e[l-1]===" ");)l--;return{k:i,vs:d,ve:l}}function nf(e,t){if(!e.includes(";")||!e.includes("="))return[];let n=[],r=0;for(;r<=e.length;){let o=e.indexOf(`
|
|
7
|
-
`,r);o<0&&(o=e.length);let s=[],i=r,d=!1;for(;i<o;){let l=e.indexOf(";",i);l<0||l>o?l=o:d=!0;let c=
|
|
3
|
+
`,o=Buffer.from(r,"utf8");if(o.byteLength>t)throw new he(n.kind,o.byteLength,t);return{bytes:o}}function ma(e,t){let n=(0,or.resolve)(t),r=(0,or.resolve)(e);if(r!==n&&!r.startsWith(n+or.sep))throw new Error("Path escapes .ai-access storage root");return r}function bn(e,t,n){let r=ma(t,e),o=`${r}.${process.pid}.tmp`;(0,ft.writeFileSync)(o,n.bytes,{mode:zr}),(0,ft.renameSync)(o,r),(0,ft.chmodSync)(r,zr)}function ga(e,t,n){let r=ma(t,e);(0,ft.appendFileSync)(r,n.bytes,{mode:zr});try{(0,ft.chmodSync)(r,zr)}catch{}}var ft,or,_s,zr,Y,ha,he,de=W(()=>{"use strict";y();ft=require("fs"),or=require("path"),_s=448,zr=384,Y=15e5,ha=8192,he=class extends Error{constructor(n,r,o){super(`${n} size ${r} bytes exceeds limit of ${o} bytes`);this.kind=n;this.size=r;this.maxBytes=o;this.name="StoragePayloadTooLargeError"}static{a(this,"StoragePayloadTooLargeError")}code="payload_too_large";statusCode=413};a(St,"encodeTrustedJson");a(ma,"assertUnderAiAccess");a(bn,"writeTrustedAtomic");a(ga,"appendTrusted")});function ir(e){if(typeof e!="string"||e.length===0||e.length>2048)return;let t;try{t=new URL(e)}catch{return}if(!(t.protocol!=="http:"&&t.protocol!=="https:")&&!(t.username||t.password||t.search||t.hash)&&!(t.pathname!=="/"||/^[a-z][a-z0-9+.-]*:\/\/[^/]*\//i.test(e)))return t.origin.toLowerCase()}function Tu(e,t){let n=e.getHeader("Vary"),r=String(n??"").split(",").map(o=>o.trim()).filter(Boolean);r.some(o=>o.toLowerCase()===t.toLowerCase())||r.push(t),e.setHeader("Vary",r.join(", "))}function wa(e){return(t,n,r)=>{if(!Au.test(t.path)){r();return}for(let c of n.getHeaderNames())c.startsWith("access-control-")&&n.removeHeader(c);let o=n.setHeader.bind(n),s=!1;n.setHeader=((c,u)=>!s&&/^access-control-/i.test(c)?n:o(c,u));let i=ir(t.get("origin")),d=i===void 0?void 0:e().find(c=>c===i),l=d!==void 0;if(Tu(n,"Origin"),!l){if(t.method==="OPTIONS"&&t.get("access-control-request-method")){n.status(204).end();return}r();return}if(s=!0,n.setHeader("Access-Control-Allow-Origin",d),n.setHeader("Access-Control-Expose-Headers",Ru),t.method==="OPTIONS"&&t.get("access-control-request-method")){n.setHeader("Access-Control-Allow-Methods",xu),n.setHeader("Access-Control-Allow-Headers",Su),n.setHeader("Access-Control-Max-Age",String(600)),n.status(204).end();return}s=!1,r()}}function ba(e,t){let n=e.parent,r=n&&typeof n.use=="function"?n:e;r.use(t);let o=r,i=(o._router??o.router)?.stack;if(!Array.isArray(i)||i.length<2)return;let d=i.pop(),l=0;for(;l<i.length;){let c=i[l].name;if(c!=="query"&&c!=="expressInit")break;l+=1}i.splice(l,0,d)}var Au,xu,Su,Ru,Jr=W(()=>{"use strict";y();Au=/^\/mcp(?:\/?$|\/(?:a(?:\/|$)|api(?:\/|$)|rpc\/?$))/,xu="GET, POST, DELETE, OPTIONS",Su="Authorization, Content-Type, Accept, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID",Ru="Mcp-Session-Id, WWW-Authenticate, Retry-After, RateLimit, RateLimit-Policy, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset";a(ir,"canonicalOrigin");a(Tu,"appendVary");a(wa,"aiCorsMiddleware");a(ba,"installAiCorsMiddleware")});function Yr(e){return e.replace(/([a-z])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/([A-Za-z])([0-9])/g,"$1 $2").replace(/([0-9])([A-Za-z])/g,"$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean).map(t=>t.toUpperCase())}function dr(e){if(!Array.isArray(e))return;let t=[];for(let n of e){if(typeof n!="string")continue;let r=n.trim().toUpperCase();if(!(!Vr.test(r)||t.includes(r))&&(t.push(r),t.length>=ar))break}return t}var X,Wt,Vr,ar,vn=W(()=>{"use strict";y();X="[REDACTED]",Wt=Object.freeze(["PASSWORD","PASSWD","PASS","PWD","PW","PASSPHRASE","SECRET","CLIENT_SECRET","TOKEN","API_KEY","APIKEY","KEY","PRIVATE","CREDENTIAL","AUTH","AUTHORIZATION","BEARER","SESSION","COOKIE","CONNECTION_STRING","CONN_STR","CONN","DSN"]),Vr=/^[A-Za-z0-9_-]{2,40}$/,ar=100;a(Yr,"tokenizeName");a(dr,"normalizeSecretNamePatterns")});function _u(e){return e==="nodes"||e==="flows"||e==="full"}function Zr(e,t){if(!e||typeof e!="object"||Array.isArray(e))return{};let n=e,r={};if(typeof n.enabled=="boolean"&&(r.enabled=n.enabled),typeof n.allowWithoutAdminAuth=="boolean"&&(r.allowWithoutAdminAuth=n.allowWithoutAdminAuth),typeof n.defaultLinkTtlSeconds=="number"&&Number.isInteger(n.defaultLinkTtlSeconds)&&n.defaultLinkTtlSeconds>0&&(r.defaultLinkTtlSeconds=Math.floor(n.defaultLinkTtlSeconds)),typeof n.proposalTtlDays=="number"&&Number.isInteger(n.proposalTtlDays)&&n.proposalTtlDays>=1&&n.proposalTtlDays<=365&&(r.proposalTtlDays=n.proposalTtlDays),_u(n.defaultDeploymentType)&&(r.defaultDeploymentType=n.defaultDeploymentType),typeof n.maxActiveLinks=="number"&&Number.isInteger(n.maxActiveLinks)&&n.maxActiveLinks>0&&(r.maxActiveLinks=Math.floor(n.maxActiveLinks)),typeof n.publicBaseUrl=="string")try{r.publicBaseUrl=xn(n.publicBaseUrl)}catch{t?.(`AI Access: invalid publicBaseUrl value ${JSON.stringify(n.publicBaseUrl)} was dropped`)}if(Array.isArray(n.corsAllowedOrigins)){let o=new Set;for(let s of n.corsAllowedOrigins){let i=ir(s);i?o.add(i):t?.(`AI Access: invalid CORS origin value ${JSON.stringify(s)} was dropped`)}r.corsAllowedOrigins=[...o]}for(let o of["rateLimitWindowMs","rateLimitMaxPerIp","rateLimitMaxPerToken"]){let s=n[o],i=o==="rateLimitWindowMs"?100:1,d=o==="rateLimitWindowMs"?36e5:1e6;typeof s=="number"&&Number.isInteger(s)&&s>=i&&s<=d&&(r[o]=s)}if(n.secretNamePatterns!==void 0){let o=dr(n.secretNamePatterns);o?(Array.isArray(n.secretNamePatterns)&&o.length<n.secretNamePatterns.length&&t?.("AI Access: invalid or duplicate secretNamePatterns entries were dropped"),r.secretNamePatterns=o):t?.("AI Access: secretNamePatterns must be a list of strings; ignored")}if(n.oauth&&typeof n.oauth=="object"&&!Array.isArray(n.oauth)){let o=n.oauth;r.oauth={},typeof o.enabled=="boolean"&&(r.oauth.enabled=o.enabled);for(let i of["accessTokenTtlSec","refreshIdleTtlSec","dcrMaxClients"]){let d=o[i];typeof d=="number"&&Number.isInteger(d)&&d>0&&d<=31536e3&&(r.oauth[i]=d)}let s=o.refreshMaxLifetimeSec;typeof s=="number"&&Number.isInteger(s)&&s>=0&&s<=31536e4&&(r.oauth.refreshMaxLifetimeSec=s)}return r}function Cu(e,t){if(!(0,cr.existsSync)(e))return{};try{let n=(0,cr.readFileSync)(e,"utf8");return Zr(JSON.parse(n),t)}catch(n){return t?.(`AI Access: config.json is corrupt or unreadable; defaults are used (${String(n)})`),{}}}function Xr(e,t){let n={},r={...lr};for(let s of Object.keys(lr).filter(i=>i!=="oauth"))n[s]=e[s]!==void 0?"settings.js":t[s]!==void 0?"disk":"default",Object.assign(r,{[s]:e[s]??t[s]??lr[s]});let o={...r,sources:n};return e.oauth!==void 0||t.oauth!==void 0?(o.oauth={...kn,...t.oauth??{},...e.oauth??{}},n.oauth=e.oauth!==void 0?"settings.js":"disk"):(o.oauth={...kn},n.oauth="default"),o}function Is(e,t){let n=Zr(e??{},t);for(let o of Object.keys(lr).filter(s=>s!=="oauth"))e&&Object.hasOwn(e,o)&&n[o]===void 0&&Object.assign(n,{[o]:lr[o]});let r=e?.oauth;if(r&&typeof r=="object"&&!Array.isArray(r)){let o=n.oauth??={};for(let s of Object.keys(kn))Object.hasOwn(r,s)&&o[s]===void 0&&(t?.(`AI Access: settings.js aiAccess.oauth.${s} is invalid; using the default (${String(kn[s])})`),Object.assign(o,{[s]:kn[s]}))}return n}var cr,Es,Pu,kn,lr,An,Qr=W(()=>{"use strict";y();cr=require("fs"),Es=require("path");Re();Jr();vn();de();Pu=16384,kn={enabled:!1,accessTokenTtlSec:3600,refreshIdleTtlSec:2592e3,refreshMaxLifetimeSec:7776e3,dcrMaxClients:5},lr={enabled:!0,allowWithoutAdminAuth:!1,defaultLinkTtlSeconds:14400,proposalTtlDays:7,maxActiveLinks:10,defaultDeploymentType:"nodes",publicBaseUrl:"",corsAllowedOrigins:[],rateLimitWindowMs:6e4,rateLimitMaxPerIp:600,rateLimitMaxPerToken:300,secretNamePatterns:[...Wt],oauth:{...kn}};a(_u,"isDeploymentType");a(Zr,"sanitizePartial");a(Cu,"readDiskFile");a(Xr,"mergeAiAccessSettings");a(Is,"settingsJsPartial");An=class{constructor(t,n,r){this.paths=t;this.filePath=(0,Es.join)(t.root,"config.json"),this.settingsJs=Is(n,r),this.disk=Cu((0,cr.existsSync)(this.filePath)?this.filePath:(0,Es.join)(t.root,"settings.json"),r),this.cached=Xr(this.settingsJs,this.disk)}static{a(this,"SettingsStore")}filePath;disk;settingsJs;cached;getEffective(){return this.cached}getDisk(){return{...this.disk}}update(t){let n=Zr(t);for(let r of Object.keys(this.settingsJs))delete n[r];return this.persist(n),this.disk=n,this.cached=Xr(this.settingsJs,this.disk),this.cached}patch(t,n=[]){if(t.defaultLinkTtlSeconds!==void 0&&(!Number.isInteger(t.defaultLinkTtlSeconds)||t.defaultLinkTtlSeconds<1))throw new Error("defaultLinkTtlSeconds must be an integer with a minimum of 1");if(t.proposalTtlDays!==void 0&&(!Number.isInteger(t.proposalTtlDays)||t.proposalTtlDays<1||t.proposalTtlDays>365))throw new Error("proposalTtlDays must be an integer between 1 and 365");let r=Zr(t),o={...this.disk,...r,...r.oauth?{oauth:{...this.disk.oauth,...r.oauth}}:{}};for(let s of n)delete o[s];return this.update(o)}persist(t){let n=St(t,Pu,{kind:"settings",pretty:!0});bn(this.paths.root,this.filePath,n)}}});function to(e){eo=e}function va(){return eo}function ae(e){return e.adminAuth!=null&&e.adminAuth!==!1}function Me(e){return eo?eo.getEffective():Xr(Is(e.aiAccess),{})}function De(e){let t=Me(e);return t.enabled?ae(e)?!0:t.allowWithoutAdminAuth:!1}var eo,Te=W(()=>{"use strict";y();Qr();eo=null;a(to,"bindSettingsStore");a(va,"getSettingsStore");a(ae,"isAdminAuthEnabled");a(Me,"resolveAiAccessSettings");a(De,"areAiFeaturesEnabled")});function Pe(e){let t=`/${e??""}`.replace(/\/{2,}/g,"/");for(;t.length>1&&t.endsWith("/");)t=t.slice(0,-1);return t}function _e(e){let t=Pe(e);return t==="/"?"/mcp/api/v1":`${t}/mcp/api/v1`}function ur(e){let t=Pe(e);return t==="/"?"/ai-access":`${t}/ai-access`}function Rt(e){let t=Pe(e);return t==="/"?"/mcp/rpc":`${t}/mcp/rpc`}function Eu(e){let t=e.authInfo?.scope;if(t)return Array.isArray(t)?t:[t];let n=e.user?.permissions;return n?Array.isArray(n)?n:[n]:[]}function Tt(e){if(e.auth)return e.auth.scopes.includes("propose")?"propose":e.auth.scopes.includes("read")?"read":null;let t=Eu(e);return t.includes("*")?"*":t.includes("read")?"read":null}function Iu(e,t){return!t||!e?{view:!0,manageLinks:!1,approveProposals:!1}:e==="*"?{view:!0,manageLinks:!0,approveProposals:!0}:{view:!0,manageLinks:!1,approveProposals:!1}}function ka(e,t={error:"Authentication required",code:"auth_required"},n){let r=e.end.bind(e),o=!0;return e.end=((...s)=>o&&e.statusCode===401&&!e.headersSent?(o=!1,e.removeHeader("Content-Length"),e.setHeader("Content-Type","application/json; charset=utf-8"),n&&e.setHeader("WWW-Authenticate",n),r(JSON.stringify(t))):r(...s)),()=>{o=!1,e.end=r}}function Ce(e,t){let n="read";return(r,o,s)=>{if(!ae(e.settings)){s();return}let i=t==="api"?ka(o):null;e.auth.needsPermission(n)(r,o,d=>{if(i?.(),d){s(d);return}if(!r.user){if(t==="page"){o.redirect(Pe(e.settings.httpAdminRoot));return}o.status(401).json({error:"Authentication required",code:"auth_required"});return}if(!Tt(r)){o.status(401).json({error:"Insufficient permissions",code:"insufficient_permissions"});return}s()})}}function no(e,t){let n=ae(e.settings),r=Me(e.settings),o=De(e.settings),s=null;return n?s=Tt(t):o&&(s="*"),{adminAuthEnabled:n,aiFeaturesEnabled:o,allowWithoutAdminAuth:r.allowWithoutAdminAuth,permission:s,capabilities:Iu(s,o)}}function Sn(e){return(t,n,r)=>{if(!De(e.settings)){n.status(403).json({error:"AI Access features are disabled. Enable adminAuth, or set aiAccess.allowWithoutAdminAuth to true for local-only use.",code:"ai_features_disabled"});return}r()}}function Rn(e){return(t,n,r)=>{if(!ae(e.settings)){if(De(e.settings)){r();return}n.status(403).json({error:"AI Access features are disabled",code:"ai_features_disabled"});return}if(Tt(t)!=="*"){n.status(403).json({error:"Full admin permission (*) is required for this action",code:"insufficient_permissions"});return}r()}}function Lu(e){return e.startsWith("aia_")}function Nu(e){return Lu(e)?!0:e.length<24}function Fu(e){if(e.length<24||e.startsWith("aia_")&&e.length<40)return"The token may be truncated \u2014 ask the user for the full access-link token."}function Aa(e,t,n,r){if(!ae(e.settings)){r();return}let o={error:He,code:ht},s=ka(n,o,ge);e.auth.needsPermission("read")(t,n,i=>{if(s(),i){r(i);return}if(!n.headersSent){if(!t.user||!Tt(t)){n.setHeader("WWW-Authenticate",ge),n.status(401).json(o);return}r()}})}function xa(e,t,n,r,o){let s=e.headers.authorization;if(typeof s!="string"||!/^Bearer\s+\S+/i.test(s.trim()))return"continue";let d=/^Bearer\s+(\S+)$/i.exec(s.trim())?.[1];if(!d)return"continue";let l=n(d);if(l.status==="active")return l.link.kind!==r?(hr(t,r,l.link.kind),"wrong_kind"):(e.accessLink=l.link,"ok");if(l.status==="expired")return fr(e,t,o,l.expiresAt),"invalid";if(Nu(d)){t.setHeader("WWW-Authenticate",ge);let c=Fu(d);return t.status(401).json({error:zt,code:pr,...c?{hint:c}:{}}),"invalid"}return"continue"}function Sa(e,t,n="rest"){return(r,o,s)=>{if(!De(e.settings)){o.status(403).json({error:"AI Access features are disabled. Enable adminAuth, or set aiAccess.allowWithoutAdminAuth to true for local-only use.",code:"ai_features_disabled"});return}let i=xa(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||Aa(e,r,o,s)}}function Ra(e,t,n="rest"){return(r,o,s)=>{let i=xa(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||Aa(e,r,o,s)}}var Ye=W(()=>{"use strict";y();Gt();Te();a(Pe,"normalizeHttpAdminRoot");a(_e,"mcpApiBase");a(ur,"dashboardPath");a(Rt,"mcpRpcPath");a(Eu,"scopesFromRequest");a(Tt,"resolvePermission");a(Iu,"capabilitiesFor");a(ka,"interceptNeedsPermissionUnauthorized");a(Ce,"requireAdminSession");a(no,"describeAccess");a(Sn,"requireAiFeatures");a(Rn,"requireStarPermission");a(Lu,"looksLikeAccessLinkToken");a(Nu,"looksLikeFailedAccessLinkAttempt");a(Fu,"truncatedTokenHint");a(Aa,"continueAsAdminSessionOrAccessLinkMissing");a(xa,"tryAccessLinkBearer");a(Sa,"requireAdminReadOrAccessLink");a(Ra,"requireAdminSessionOrAccessLink")});function Ou(e,t){let n=Pe(e);return`${n==="/"?"/mcp/a":`${n}/mcp/a`}/${t}`}function $u(e){return!!e.app.get("trust proxy")}function Ta(e,t){let n=e.get(t);if(typeof n!="string"||n.length===0)return;let r=n.split(",")[0]?.trim();return r&&r.length>0?r:void 0}function Mu(e){if(typeof e!="string"||e.trim().length===0)return;let t;try{t=new URL(e.trim())}catch{return}if(!(t.protocol!=="http:"&&t.protocol!=="https:"))return`${t.origin}${t.pathname.replace(/\/+$/,"")}`}function Kt(e){try{let n=new URL(`http://${e.includes(":")&&!e.startsWith("[")?`[${e}]`:e}`).hostname.replace(/^\[|\]$/g,"");if(!(0,Ls.isIP)(n))return;if(n.startsWith("::ffff:")){let r=n.slice(7);if((0,Ls.isIP)(r)===4)return r;let o=r.split(":");if(o.length===2){let s=parseInt(o[0],16)*65536+parseInt(o[1],16);return[24,16,8,0].map(i=>s>>>i&255).join(".")}}return n}catch{return}}function xn(e){if(e==="")return"";let t="publicBaseUrl must be an HTTP(S) origin with no path, query, fragment, or credentials";if(typeof e!="string")throw new Error(t);let n;try{n=new URL(e.trim())}catch{throw new Error(t)}if(n.protocol!=="https:"&&n.protocol!=="http:"||!/^https?:\/\/[^/?#]+\/?$/i.test(e.trim())||n.pathname!=="/"||n.search||n.hash||n.username||n.password||e.includes("?")||e.includes("#"))throw new Error(t);return n.origin}function ro(e){Ns=Mu(e)}function _a(e){if(typeof e!="string"||e.length===0||e.length>260)return!1;let t,n,r=/^(\[[^\]]*\])(?::(\d+))?$/.exec(e);if(r){if(t=r[1],n=r[2],!ju.test(t))return!1;try{new URL(`http://${t}/`)}catch{return!1}}else{let o=/^([^:]+)(?::(\d+))?$/.exec(e);if(!o||(t=o[1],n=o[2],t.length>253)||!Hu.test(t)&&!Du.test(t))return!1}if(n!==void 0){if(!/^\d{1,5}$/.test(n))return!1;let o=Number(n);if(o<1||o>65535)return!1}return!0}function Ea(e){let t=e?.trim().toLowerCase();return t==="http"||t==="https"?t:void 0}function Uu(e){let t=e.socket;return t?.encrypted?"https":t?"http":Ea(e.protocol)??"http"}function Fs(e){if(Ns)return Ns;let t=$u(e),n=t?Ta(e,"x-forwarded-host"):void 0,r=e.get("host")?.trim(),o=_a(n)?n:_a(r)?r:void 0;if(!o)return;let s=(t?Ea(Ta(e,"x-forwarded-proto")):void 0)??Uu(e);try{return new URL(`${s}://${o}`).origin}catch{return}}function mr(e,t){let n=Fs(e);return n?`${n}${t}`:t}function Ia(e,t,n){return mr(e,Ou(t,n))}function Os(e){return e.replace(/\/mcp\/a\/[^/?#]+/gi,"/mcp/a/<redacted>")}var Ls,Tn,Ca,Ns,Pa,Du,Hu,ju,Re=W(()=>{"use strict";y();Ls=require("net");Ye();Tn=8192,Ca=1800*1e3;a(Ou,"mcpAccessLinkPath");a($u,"isTrustProxyEnabled");a(Ta,"firstForwardedValue");a(Mu,"normalizePublicBaseUrl");a(Kt,"canonicalizeIp");a(xn,"validateSettingsPublicBaseUrl");a(ro,"bindPublicBaseUrl");Pa="[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?",Du=new RegExp(`^${Pa}(?:\\.${Pa})*$`),Hu=/^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/,ju=/^\[[0-9A-Fa-f:.]+\]$/;a(_a,"isValidHostHeader");a(Ea,"validScheme");a(Uu,"connectionScheme");a(Fs,"requestPublicOrigin");a(mr,"absolutizePath");a(Ia,"buildAccessLinkUrl");a(Os,"redactAccessLinkTokenInPath")});function Ku(e){if(!e||e.trim().length===0)return{kind:"missing"};let t=e.trim();if(/^Basic\s+/i.test(t))return{kind:"basic"};let n=/^Bearer\s+(\S+)$/i.exec(t);return n?.[1]?{kind:"bearer",token:n[1]}:{kind:"other"}}function $s(e,t,n,r){e.setHeader("WWW-Authenticate",ge),e.status(401).json({error:t,code:n,...r?{hint:r}:{}})}function Ju(e){let t=e??"";for(;t.endsWith("/");)t=t.slice(0,-1);return`${t}/ai-access#links`}function Vu(e,t,n,r){let o=mr(e,Ju(t));return{error:Bu,code:Wu,...n?{expiredAt:n}:{},accessLinksUrl:o,hint:r??`Ask the user to create a new access link in Node-RED on the /ai-access dashboard \u2192 Access tokens (${o}).`}}function fr(e,t,n,r,o){t.setHeader("WWW-Authenticate",ge),t.status(401).json(Vu(e,n,r,o))}function Yu(e){if(e.length<24||e.startsWith("aia_")&&e.length<40)return"The token may be truncated \u2014 ask the user for the full access-link token."}function oo(e){return e==="get_link"?"Please open Node-RED \u2192 /ai-access \u2192 Access tokens and create a get_link access URL, then send me that /mcp/a/<token> link.":`Please open Node-RED \u2192 /ai-access \u2192 Access tokens and create a kind=${e} token, then send me the Bearer token (not a get_link path URL).`}function Xu(e,t){let n=Zu;return e==="get_link"?`This URL requires a get_link token. This token is kind=${t} \u2014 ${n} for a get_link access URL (/mcp/a/<token>/\u2026). What to tell the user: ${oo("get_link")}`:e==="rest"?`This REST endpoint requires a rest token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${oo("rest")}`:`This MCP endpoint requires an mcp token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${oo("mcp")}`}function hr(e,t,n){e.setHeader("WWW-Authenticate",ge),e.status(403).json({error:`Access link kind mismatch: expected ${t}, got ${n}`,code:Gu,expectedKind:t,actualKind:n,hint:Xu(t,n),tellUser:oo(t)})}function Pn(e,t,n){return(r,o,s)=>{if(!De(e.settings)){o.status(403).json({error:"AI Access features are disabled. Enable adminAuth, or set aiAccess.allowWithoutAdminAuth to true for local-only use.",code:"ai_features_disabled"});return}let i=Ku(r.headers.authorization);if(i.kind==="basic"){$s(o,qu,zu);return}if(i.kind==="missing"||i.kind==="other"){$s(o,He,ht);return}let d=t.validateResult(i.token);if(d.status==="expired"){fr(r,o,e.settings.httpAdminRoot,d.expiresAt);return}if(d.status!=="active"){$s(o,zt,pr,Yu(i.token));return}if(d.link.kind!==n){hr(o,n,d.link.kind);return}r.accessLink=d.link,s()}}var zt,Bu,He,qu,ge,ht,pr,Wu,zu,Gu,Zu,Gt=W(()=>{"use strict";y();Re();Te();zt="Access link is invalid, revoked, or no longer available (it may have expired, or Node-RED may have restarted). Ask the user for a new access link.",Bu="Access link token has expired. Ask the user for a new access link.",He="Access link Bearer token is missing. Ask the user for a new access link and send Authorization: Bearer <token>.",qu="Authorization must use the Bearer scheme (Authorization: Bearer <access-link-token>), not Basic. Ask the user for a new access link.",ge='Bearer realm="node-red-ai-access", charset="UTF-8"',ht="access_link_missing",pr="access_link_invalid",Wu="token_expired",zu="bearer_scheme_required",Gu="wrong_token_kind";a(Ku,"parseAuthorizationHeader");a($s,"sendUnauthorized");a(Ju,"accessLinksPagePath");a(Vu,"tokenExpiredBody");a(fr,"sendTokenExpired");a(Yu,"truncatedBearerHint");Zu="ask the owner: Node-RED /ai-access \u2192 Access tokens \u2192 create kind=rest/mcp (or get_link)";a(oo,"whatToTellUserWrongTokenKind");a(Xu,"wrongTokenKindHint");a(hr,"sendWrongTokenKind");a(Pn,"requireAccessLink")});var re,te,je,Ze,io,Ee=W(()=>{"use strict";y();re=class extends Error{static{a(this,"FlowRevisionConflictError")}code="version_mismatch";statusCode=409;expectedRev;currentRev;context;constructor(t,n,r="updateFlow"){super(`Flow revision conflict in ${r}: expectedRev '${t}' does not match current rev '${n}'. The flows changed since you last read them \u2014 re-read flows, re-apply your change, and retry.`),this.name="FlowRevisionConflictError",this.expectedRev=t,this.currentRev=n,this.context=r}},te=class extends Error{static{a(this,"FlowNotFoundError")}code="flow_not_found";statusCode=404;flowId;constructor(t){super(`Flow '${t}' was not found`),this.name="FlowNotFoundError",this.flowId=t}},je=class extends Error{static{a(this,"AdapterUnavailableError")}code="adapter_unavailable";statusCode=502;constructor(t){super(t),this.name="AdapterUnavailableError"}},Ze=class extends Error{static{a(this,"PluginMisconfiguredError")}code="plugin_misconfigured";statusCode=502;constructor(t){super(t),this.name="PluginMisconfiguredError"}},io="Node-RED AI Access could not use the in-process flow runtime and the HTTP Admin API fallback returned Unauthorized. The plugin is misconfigured \u2014 tell the admin to ensure the plugin loads inside Node-RED so RED.runtime.flows is available (do not rely on unauthenticated localhost HTTP when adminAuth is enabled)."});function Xe(e,t){let n=t.match(/\["(?:[^"\\]|\\.)*"\]|[^.[\]]+/g)??[],r=e;for(let o of n)if(o.startsWith("[")){let s=JSON.parse(o.slice(1,-1));r=Array.isArray(r)?r.find(i=>!!i&&typeof i=="object"&&i.name===s):void 0}else r=r&&typeof r=="object"&&Object.hasOwn(r,o)?r[o]:void 0;return r}var ao=W(()=>{"use strict";y();a(Xe,"conflictFieldValue")});function Qe(e,t){if(!e||!Qu.test(e)||e.includes(".."))throw new Error(`Invalid ${t}`);return e}function lo(e){if(!e||!ep.test(e)||e.includes(".."))throw new Error("Invalid proposal id");return e}function Ms(e,t){let n=(0,Vt.basename)(t);if(!n||n!==t||n.includes(".."))throw new Error("Invalid file name");let r=(0,Vt.resolve)(e),o=(0,Vt.resolve)(r,n);if(o!==r&&!o.startsWith(r+Vt.sep))throw new Error("Path escapes storage directory");return o}function Jt(e,t=1e5){if(typeof e=="string")return e.replace(/\0/g,"").slice(0,t)}function Ue(e){let t=e&&typeof e=="object"&&!Array.isArray(e)?e:{},n={};for(let o of tp){if(!(o in t))continue;let s=t[o];if(o==="disabled"&&typeof s=="boolean"){n[o]=s;continue}if(o==="env"&&Array.isArray(s)){let d=JSON.stringify(s);d.length<=5e5&&(n[o]=JSON.parse(d));continue}let i=Jt(s,1e4);i!==void 0&&(n[o]=i)}for(let o of["nodes","configs"]){let s=t[o];if(!Array.isArray(s))continue;let i=JSON.stringify(s),d=Buffer.byteLength(i,"utf8");if(d>Y)throw new he(`flow.${o}`,d,Y);n[o]=JSON.parse(i)}let r=Buffer.byteLength(JSON.stringify(n),"utf8");if(r>Y)throw new he("flow",r,Y);return n}function La(e){let t={};Array.isArray(e.skippedAttempts)&&(t.skippedAttempts=e.skippedAttempts.map(n=>{let r=n;return{proposalId:Jt(r.proposalId,200),attemptId:Jt(r.attemptId,200),flowId:Jt(r.flowId,200)}})),Array.isArray(e.changedFlowIds)&&(t.changedFlowIds=e.changedFlowIds.map(n=>Jt(n,200)).filter(n=>n!==void 0)),Array.isArray(e.affectedProposalIds)&&(t.affectedProposalIds=e.affectedProposalIds.map(n=>Jt(n,200)).filter(n=>n!==void 0)),typeof e.rejectedWhileDeployUnknown=="boolean"&&(t.rejectedWhileDeployUnknown=e.rejectedWhileDeployUnknown);for(let n of np){if(!(n in e))continue;let r=e[n];if(rp.has(n)){if(typeof r=="number"&&Number.isFinite(r))t[n]=r;else if(typeof r=="string"&&r.trim()!==""){let s=Number(r);Number.isFinite(s)&&(t[n]=s)}continue}let o=Jt(r,2e3);o!==void 0&&(t[n]=o)}return t}var Vt,Qu,ep,tp,np,rp,co=W(()=>{"use strict";y();Vt=require("path");de();Qu=/^[A-Za-z0-9_.-]+$/,ep=/^prop-[A-Za-z0-9_-]+$/,tp=["id","type","label","disabled","info","env"],np=["proposalId","attemptId","withdrawalAuthority","originalOwnerId","flowId","expectedRev","resultRev","reason","code","currentRev","title","displayName","flowLabel","linkId","label","expiresAt","createdAt","ttlSeconds","kind","redactions","restoredRedactions","operation"],rp=new Set(["ttlSeconds","redactions","restoredRedactions"]);a(Qe,"assertSafeSegment");a(lo,"assertSafeProposalId");a(Ms,"resolveUnderDir");a(Jt,"sanitizeString");a(Ue,"sanitizeFlowTab");a(La,"sanitizeAuditDetails")});function U(e){return e==="pending"||e==="conflict"}function fo(e,t){return`oauth-${(0,gr.createHash)("sha256").update(`${e}\0${t}`).digest("hex")}`}function mt(e){return e.kind==="oauth"?fo(e.clientId,e.user):e.linkId}function op(e){return e==="pending"||e==="conflict"||e==="approved"||e==="rejected"||e==="failed"||e==="withdrawn"||e==="expired"||e==="superseded"}function Ie(e,t){return typeof e=="string"&&e.length>0?e.replace(/\0/g,"").slice(0,t):void 0}function ip(e){if(!e||typeof e!="object"||Array.isArray(e))return;let t=e,n=Ie(t.rev,200),r=t.state;if(!n||r!=="clean"&&r!=="merged"&&r!=="conflict")return;let o=[];if(Array.isArray(t.conflicts))for(let d of t.conflicts.slice(0,200)){if(!d||typeof d!="object")continue;let l=d,c=Ie(l.kind,64),u=Ie(l.message,500);if(!c||!u)continue;let p={kind:c,message:u,choices:Array.isArray(l.choices)?l.choices.filter(g=>g==="live"||g==="proposed"):[]},f=Ie(l.nodeId,200);f&&(p.nodeId=f);let h=Ie(l.nodeType,200);h&&(p.nodeType=h);let m=Ie(l.nodeName,120);m&&(p.nodeName=m),Array.isArray(l.fields)&&(p.fields=l.fields.filter(g=>typeof g=="string"&&g.length>0).slice(0,100).map(g=>g.replace(/\0/g,"").slice(0,200))),o.push(p)}let s={rev:n,state:r,conflicts:o,liveChangedCount:typeof t.liveChangedCount=="number"&&Number.isFinite(t.liveChangedCount)?Math.max(0,Math.floor(t.liveChangedCount)):0,checkedAt:Ie(t.checkedAt,64)??""},i=Ie(t.mergedHash,64);return i&&/^[0-9a-f]+$/.test(i)&&(s.mergedHash=i),s}function ap(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e.slice(0,200)){if(!n||typeof n!="object")continue;let{nodeId:r,property:o}=n;typeof r!="string"||typeof o!="string"||t.push({nodeId:r.replace(/\0/g,"").slice(0,200),property:o.replace(/\0/g,"").slice(0,200)})}return t.length>0?t:void 0}function Ds(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e.slice(0,sp)){if(!n||typeof n!="object"||Array.isArray(n))continue;let r=n;if(typeof r.code!="string"||!r.code||typeof r.error!="string"||!r.error)continue;let o={code:r.code.replace(/\0/g,"").slice(0,80),error:r.error.replace(/\0/g,"").slice(0,500)};typeof r.nodeId=="string"&&r.nodeId&&(o.nodeId=r.nodeId.replace(/\0/g,"").slice(0,200)),typeof r.type=="string"&&r.type&&(o.type=r.type.replace(/\0/g,"").slice(0,200)),typeof r.property=="string"&&r.property&&(o.property=r.property.replace(/\0/g,"").slice(0,200)),typeof r.configType=="string"&&r.configType&&(o.configType=r.configType.replace(/\0/g,"").slice(0,200)),Array.isArray(r.candidates)&&(o.candidates=r.candidates.filter(s=>typeof s=="string"&&s.length>0).slice(0,20).map(s=>s.replace(/\0/g,"").slice(0,200))),t.push(o)}return t.length>0?t:void 0}function Na(e){if(!Array.isArray(e))return;let t=e.filter(n=>{if(!n||typeof n!="object"||Array.isArray(n))return!1;let r=n;return typeof r.nodeId=="string"&&Array.isArray(r.fields)});return t.length?t.map(n=>({nodeId:n.nodeId.replace(/\0/g,"").slice(0,200),fields:n.fields.filter(r=>typeof r=="string").map(r=>r.replace(/\0/g,"").slice(0,500))})):void 0}var gr,Pt,Fa,Oa,uo,ye,Q,_t,Le,sp,et,po,tt=W(()=>{"use strict";y();gr=require("crypto"),Pt=require("fs"),Fa=require("path"),Oa=require("util");ao();co();de();uo=(0,gr.randomBytes)(16).toString("hex");a(U,"isOpenProposalStatus");a(fo,"oauthOwnerId");a(mt,"proposalOwnerId");ye=class extends Error{static{a(this,"ProposalNotFoundError")}code="proposal_not_found";statusCode=404;constructor(t){super(`Proposal '${t}' was not found`),this.name="ProposalNotFoundError"}},Q=class extends Error{static{a(this,"ProposalNotPendingError")}code="proposal_not_pending";statusCode=409;constructor(t,n){super(`Proposal '${t}' is '${n}', not pending`),this.name="ProposalNotPendingError"}},_t=class extends Error{static{a(this,"ProposalNotOwnedError")}code="proposal_not_owned";statusCode=403;constructor(t){super(`Proposal '${t}' was not created by this access link`),this.name="ProposalNotOwnedError"}},Le=class extends Error{static{a(this,"ProposalConcurrentApproveError")}code="proposal_approve_in_progress";statusCode=409;constructor(t){super(`Proposal '${t}' is already being approved`),this.name="ProposalConcurrentApproveError"}};a(op,"isProposalStatus");sp=50;a(Ie,"cleanText");a(ip,"sanitizeRebaseInfo");et=class extends Error{static{a(this,"ProposalConflictError")}code="proposal_conflict";statusCode=409;conflicts;constructor(t,n){super(`Proposal '${t}' conflicts with changes made in the editor since it was submitted; resolve the conflicts before approving.`),this.name="ProposalConflictError",this.conflicts=n}};a(ap,"sanitizeRestoredSecrets");a(Ds,"sanitizeProposalWarnings");a(Na,"sanitizeAiOverrides");po=class{constructor(t,n={}){this.paths=t;this.now=n.now??(()=>Date.now()),this.randomId=n.randomId??(()=>(0,gr.randomBytes)(12).toString("hex")),this.maxProposalBytes=n.maxProposalBytes??Y}static{a(this,"ProposalStore")}inFlight=new Set;now;randomId;maxProposalBytes;fileFor(t){let n;try{n=lo(t)}catch{throw new ye(t)}return Ms(this.paths.proposals,`${n}.json`)}toDiskRecord(t){let n=lo(t.id),r=Qe(String(t.expectedRev).slice(0,200),"expectedRev"),o=Qe(String(t.payload.flowId).slice(0,200),"flowId"),s=Ue(t.payload.flow),i=t.baseFlow!==void 0?Ue(t.baseFlow):void 0,d=t.createdBy.kind==="oauth"?{kind:"oauth",clientId:Qe(String(t.createdBy.clientId).slice(0,200),"clientId"),user:String(t.createdBy.user).replace(/\0/g,"").slice(0,200)}:{kind:"access-link",linkId:Qe(String(t.createdBy.linkId).slice(0,200),"linkId"),...typeof t.createdBy.label=="string"?{label:t.createdBy.label.replace(/\0/g,"").slice(0,200)}:{}},l={id:n,type:"replace_flow",status:op(t.status)?t.status:"pending",expectedRev:r,payload:{flowId:o,flow:s},createdBy:d,createdAt:String(t.createdAt).slice(0,64),updatedAt:String(t.updatedAt).slice(0,64)};i!==void 0&&(l.baseFlow=i),typeof t.title=="string"&&t.title.length>0&&(l.title=t.title.replace(/\0/g,"").slice(0,80).trim(),l.title||delete l.title);let c=Ds(t.warnings);c&&(l.warnings=c),t.landedAfterWithdraw===!0&&(l.landedAfterWithdraw=!0),typeof t.deploymentAlert=="string"&&(l.deploymentAlert=Ie(t.deploymentAlert,500)??"");let u=ap(t.restoredSecrets);if(u&&(l.restoredSecrets=u),t.deployPending?.bootId===uo){let m=Ie(t.deployPending.mergedHash,64),g=Ie(t.deployPending.reason,500);if(m&&/^[0-9a-f]+$/.test(m)&&g){l.deployPending={mergedHash:m,reason:g,bootId:uo};let w=Ie(t.deployPending.attemptId,36);w&&(l.deployPending.attemptId=w),t.deployPending.queueReleased===!0&&(l.deployPending.queueReleased=!0)}}let p=Na(t.aiConflictOverrides);p&&U(l.status)&&(l.aiConflictOverrides=p);let f=t.conflictChoice;f&&U(l.status)&&(f.choice==="live"||f.choice==="proposed")&&typeof f.nodeId=="string"&&f.undo&&(l.conflictChoice={nodeId:f.nodeId.replace(/\0/g,"").slice(0,200),choice:f.choice,...f.chooser==="ai"||f.chooser==="human"?{chooser:f.chooser}:{},undo:{baseFlow:Ue(f.undo.baseFlow),flow:Ue(f.undo.flow),warnings:Ds(f.undo.warnings)??[],aiConflictOverrides:Na(f.undo.aiConflictOverrides)??[]}});let h=ip(t.rebase);if((h&&U(l.status)||h&&h.state!=="conflict")&&(l.rebase=h),t.decidedBy)if(t.decidedBy.kind==="access-link"){let m={kind:"access-link",linkId:Qe(String(t.decidedBy.linkId).slice(0,200),"linkId")};typeof t.decidedBy.label=="string"&&(m.label=t.decidedBy.label.replace(/\0/g,"").slice(0,200)),l.decidedBy=m}else{let m={kind:"admin"};typeof t.decidedBy.username=="string"&&(m.username=t.decidedBy.username.replace(/\0/g,"").slice(0,200)),l.decidedBy=m}return typeof t.reason=="string"&&(l.reason=t.reason.replace(/\0/g,"").slice(0,2e3)),t.rejectedWhileDeployUnknown===!0&&(l.rejectedWhileDeployUnknown=!0),typeof t.resultRev=="string"&&(l.resultRev=Qe(t.resultRev.slice(0,200),"resultRev")),l}writeAtomic(t,n){let r=this.fileFor(t),o=this.toDiskRecord(n),s=St(o,this.maxProposalBytes,{kind:"proposal",pretty:!0});bn(this.paths.root,r,s)}create(t){let n=new Date(this.now()).toISOString(),r=lo(`prop-${this.randomId()}`),o={id:r,type:"replace_flow",status:"pending",expectedRev:t.expectedRev,payload:{flowId:t.payload.flowId,flow:t.payload.flow},baseFlow:t.baseFlow,title:t.title,createdBy:t.createdBy,createdAt:n,updatedAt:n},s=Ds(t.warnings);return s&&(o.warnings=s),t.restoredSecrets?.length&&(o.restoredSecrets=t.restoredSecrets),this.writeAtomic(r,o),this.get(r)}currentProcessProposal(t){return t.deployPending?.bootId!==uo&&delete t.deployPending,t}get(t){let n=this.fileFor(t);if(!(0,Pt.existsSync)(n))throw new ye(t);return this.currentProcessProposal(JSON.parse((0,Pt.readFileSync)(n,"utf8")))}list(t){if(!(0,Pt.existsSync)(this.paths.proposals))return[];let n=(0,Pt.readdirSync)(this.paths.proposals).filter(o=>o.endsWith(".json")),r=[];for(let o of n){let s=(0,Fa.basename)(o);if(!(s!==o||s.includes("..")))try{let i=JSON.parse((0,Pt.readFileSync)(Ms(this.paths.proposals,s),"utf8"));if(t?.linkId&&mt(i.createdBy)!==t.linkId||t?.status&&i.status!==t.status||t?.flowId&&i.payload.flowId!==t.flowId)continue;r.push(this.currentProcessProposal(i))}catch{}}return r.sort((o,s)=>o.createdAt<s.createdAt?1:-1)}beginApprove(t){if(this.inFlight.has(t))throw new Le(t);let n=this.get(t);if(!U(n.status))throw new Q(t,n.status);return this.inFlight.add(t),n}releaseLock(t){this.inFlight.delete(t)}recordDeployPending(t,n){let r=this.get(t);if(n&&!U(r.status))throw new Q(t,r.status);return n?r.deployPending={...n,bootId:uo}:delete r.deployPending,U(r.status)&&(r.status="pending"),r.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,r),this.get(t)}flagRejectedWhileDeployUnknown(t){let n=this.get(t);return n.rejectedWhileDeployUnknown=!0,this.writeAtomic(t,n),this.get(t)}flagLandedAfterWithdraw(t){let n=this.get(t);n.landedAfterWithdraw=!0,this.writeAtomic(t,n)}flagDeployment(t,n){let r=this.get(t);r.deploymentAlert=n,this.writeAtomic(t,r)}markApproved(t,n,r){let o=this.get(t);return o.status="approved",delete o.deployPending,o.decidedBy=n,o.resultRev=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markRejected(t,n,r){if(this.inFlight.has(t))throw new Le(t);let o=this.get(t);return o.rejectedWhileDeployUnknown=o.deployPending?.queueReleased===!0,o.status="rejected",n&&(o.decidedBy=n),o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markFailed(t,n,r){let o=this.get(t);return o.status="failed",n&&(o.decidedBy=n),o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markWithdrawn(t,n,r){if(this.inFlight.has(t))throw new Le(t);let o=this.get(t);if(!U(o.status))throw new Q(t,o.status);return o.status="withdrawn",o.decidedBy=n,o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markClosed(t,n,r,o){let s=this.get(t);if(!U(s.status))throw new Q(t,s.status);return s.status=n,o&&(s.decidedBy=o),s.reason=r,s.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,s),this.get(t)}recordRebase(t,n){let r=this.get(t);if(!U(r.status))return r;let o=n.state==="conflict"?"conflict":"pending",s=r.rebase;return r.status===o&&s!==void 0&&s.rev===n.rev&&s.state===n.state&&s.mergedHash===n.mergedHash&&s.liveChangedCount===n.liveChangedCount&&JSON.stringify(s.conflicts)===JSON.stringify(n.conflicts)?r:(r.status=o,r.rebase=n,r.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,r),this.get(t))}updateBody(t,n){let r=this.get(t);if(!U(r.status))throw new Q(t,r.status);if(this.inFlight.has(t))throw new Le(t);r.baseFlow=n.baseFlow,r.payload={flowId:r.payload.flowId,flow:n.flow},n.expectedRev!==void 0&&(r.expectedRev=n.expectedRev),n.title!==void 0&&(r.title=n.title),n.restoredSecrets!==void 0&&(n.restoredSecrets.length>0?r.restoredSecrets=n.restoredSecrets:delete r.restoredSecrets),n.warnings!==void 0&&(n.warnings.length>0?r.warnings=n.warnings:delete r.warnings);let o=a((i,d)=>d===r.payload.flowId?i:[...i.nodes??[],...i.configs??[]].find(l=>l.id===d),"target"),s=n.aiConflictOverrides??(r.aiConflictOverrides??[]).map(i=>({nodeId:i.nodeId,fields:i.fields.filter(d=>{let l=o(n.baseFlow,i.nodeId),c=o(n.flow,i.nodeId);return!(0,Oa.isDeepStrictEqual)(d?Xe(l,d):l,d?Xe(c,d):c)})})).filter(i=>i.fields.length>0);return s.length?r.aiConflictOverrides=s:delete r.aiConflictOverrides,n.conflictChoice?r.conflictChoice=n.conflictChoice:delete r.conflictChoice,r.status="pending",delete r.rebase,r.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,r),this.get(t)}isApproving(t){return this.inFlight.has(t)}}});function $a(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new Error(`${t} must be a node object`);let n=e;if(typeof n.id!="string"||n.id.length===0)throw new Error(`${t} needs a non-empty string id`);if(typeof n.type!="string"||n.type.length===0)throw new Error(`${t} '${n.id}' needs a non-empty string type`);return n}function ho(e,t,n){let r=e.find(d=>d.id===t);if(!r||r.type!=="tab"&&r.type!=="subflow")throw new te(t);let o={...r,id:t,type:r.type};delete o.nodes,delete o.configs,delete o.subflows,n.label!==void 0&&(o.label=n.label),n.disabled!==void 0&&(o.disabled=n.disabled),n.info!==void 0&&(o.info=n.info),n.env!==void 0&&(o.env=n.env);let s=[];for(let[d,l]of(n.nodes??[]).entries())s.push({...$a(l,`flowData.nodes[${d}]`),z:t});for(let[d,l]of(n.configs??[]).entries())s.push({...$a(l,`flowData.configs[${d}]`),z:t});return[...e.filter(d=>d.id!==t&&d.z!==t),o,...s]}function yr(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new Error(`Expected ${t} to be an object with rev and flows`);let n=e;if(typeof n.rev!="string"||n.rev.length===0)throw new Error(`Expected ${t}.rev to be a non-empty string`);if(!Array.isArray(n.flows))throw new Error(`Expected ${t}.flows to be an array`);return{rev:n.rev,flows:n.flows}}function _n(e,t){if(Array.isArray(t)){let d=t.find(u=>u&&typeof u=="object"&&u.id===e&&(u.type==="tab"||u.type==="subflow"));if(!d)throw new te(e);let l=t.filter(u=>u&&typeof u=="object"&&u.id!==e&&u.z===e),c={id:e,type:d.type,nodes:l};return typeof d.label=="string"&&(c.label=d.label),typeof d.disabled=="boolean"&&(c.disabled=d.disabled),typeof d.info=="string"&&(c.info=d.info),c}if(!t||typeof t!="object")throw new te(e);let n=t,r=typeof n.id=="string"&&n.id.length>0?n.id:e,o=typeof n.type=="string"&&n.type.length>0?n.type:"tab",s=Array.isArray(n.nodes)?n.nodes:[],i={...n,id:r,type:o,nodes:s};return Array.isArray(n.configs)&&(i.configs=n.configs),i}var mo=W(()=>{"use strict";y();Ee();a($a,"asFlowNode");a(ho,"replaceFlowTabInFlatFlows");a(yr,"assertFlowsSnapshot");a(_n,"normalizeFlowTab")});function Ma(e,t=new Hs){return go in e?e:{kind:e.kind,[go]:t,async getFlows(){let r=await e.getFlows();return t.record(r),r},async getFlow(r){let o=await e.getFlow(r);if(!t.has(o.rev))try{t.record(await e.getFlows())}catch{}return o},async updateFlow(r){return e.updateFlow(r)}}}function js(e){return go in e?e[go]:void 0}function yo(e,t){return{kind:t,async getFlows(){return{rev:e.rev,flows:JSON.parse(JSON.stringify(e.flows))}},async getFlow(n){let r=JSON.parse(JSON.stringify(e.flows)),o=r.find(l=>l.id===n&&(l.type==="tab"||l.type==="subflow"));if(!o)throw new te(n);let s=[],i=[];for(let l of r){if(l.id===n||l.z!==n)continue;(l.type!=="group"&&l.x===void 0&&l.y===void 0&&l.wires===void 0?i:s).push(l)}let d={id:n,type:o.type,nodes:s};for(let l of["label","disabled","info","env"])o[l]!==void 0&&(d[l]=o[l]);return i.length>0&&(d.configs=i),{rev:e.rev,flow:d}},async updateFlow(){throw new Error("snapshot adapter is read-only")}}}var dp,Hs,go,Us=W(()=>{"use strict";y();Ee();dp=24,Hs=class{constructor(t=dp){this.maxRevs=t}static{a(this,"RevHistory")}byRev=new Map;record(t){if(!(!t||typeof t.rev!="string"||!Array.isArray(t.flows))&&!this.byRev.has(t.rev))for(this.byRev.set(t.rev,{rev:t.rev,flows:JSON.parse(JSON.stringify(t.flows))});this.byRev.size>this.maxRevs;){let n=this.byRev.keys().next().value;if(n===void 0)break;this.byRev.delete(n)}}get(t){let n=this.byRev.get(t);return n?{rev:n.rev,flows:JSON.parse(JSON.stringify(n.flows))}:void 0}has(t){return this.byRev.has(t)}},go=Symbol.for("ai-access.revHistory");a(Ma,"withRevHistory");a(js,"revHistoryOf");a(yo,"snapshotAdapter")});function zs(e){return e?lp.has(e)?!0:e.startsWith("subflow:"):!1}function Ct(e,t="node.type"){if(e==null||e==="")throw new x(`${t} is required`,"missing_node_type");if(typeof e!="string")throw new x(`${t} must be a non-empty string (got ${typeof e})`,"invalid_node_type",{details:{type:e,got:typeof e}});if(!e.trim())throw new x(`${t} is required`,"missing_node_type");return e}function gp(e,t){if(typeof e!="string"||typeof t!="string")return Number.MAX_SAFE_INTEGER;let n=e.toLowerCase(),r=t.toLowerCase();if(n===r)return 0;let o=n.length,s=r.length;if(o===0)return s;if(s===0)return o;let i=new Array(s+1),d=new Array(s+1);for(let l=0;l<=s;l++)i[l]=l;for(let l=1;l<=o;l++){d[0]=l;let c=n.charAt(l-1);for(let u=1;u<=s;u++){let p=c===r.charAt(u-1)?0:1;d[u]=Math.min((d[u-1]??0)+1,(i[u]??0)+1,(i[u-1]??0)+p)}for(let u=0;u<=s;u++)i[u]=d[u]??0}return i[s]??s}function yp(e,t){let n=e.toLowerCase(),r=t.toLowerCase(),o=Math.min(n.length,r.length),s=0;for(;s<o&&n.charAt(s)===r.charAt(s);)s+=1;return s}function bp(e){return Math.max(2,Math.floor(e.length/3))}function Gs(e,t,n=5){if(typeof e!="string"||!e)return[];let r=bp(e);return t.filter(s=>typeof s=="string"&&s!==e&&!wp.has(s)&&!s.startsWith("_")&&!s.startsWith("subflow:")).map(s=>({c:s,dist:gp(e,s),prefix:yp(e,s)})).filter(s=>s.dist<=r).sort((s,i)=>s.dist-i.dist||i.prefix-s.prefix||s.c.localeCompare(i.c)).slice(0,n).map(s=>s.c)}function vp(e){return e.endsWith("/")?e.slice(0,-1):e}function Wa(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,qa.createRequire)(e)}function kp(){try{let t=Wa()("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}function za(e){let t=e.types,n=Array.isArray(t)?t.filter(i=>typeof i=="string"&&i.length>0):[],r="";if(typeof e.module=="string"&&e.module)r=e.module;else if(typeof e.name=="string"&&e.name)r=e.name;else if(typeof e.id=="string"){let i=String(e.id).split("/")[0];r=i&&i.length>0?i:String(e.id)}let o=typeof e.id=="string"&&e.id?e.id:n.length===1&&r?`${r}/${n[0]}`:r;if(!o||n.length===0)return null;let s={id:o,module:r.length>0?r:o,types:n};return typeof e.version=="string"&&(s.version=e.version),typeof e.enabled=="boolean"&&(s.enabled=e.enabled),typeof e.name=="string"&&(s.name=e.name),s}function Ks(e=mp,t={}){let n=t.module??"node-red",r=t.version??"4.0.0",o=[...e],s=t.configs??{};return{listNodeSets(){return Promise.resolve([{id:n,module:n,version:r,types:o,enabled:!0,name:n}])},getNodeConfigHtml(i){if(s[i])return Promise.resolve(s[i]??null);let d=i.includes("/")?i.slice(i.lastIndexOf("/")+1):i;return s[d]?Promise.resolve(s[d]??null):Promise.resolve(null)}}}function Ga(e={}){if(kp())try{let n=Wa()("@node-red/runtime");if(n.nodes&&typeof n.nodes.getNodeList=="function")return new Ws(n.nodes)}catch{}if(e.baseUrl)return new qs({baseUrl:e.baseUrl,...e.accessToken?{accessToken:e.accessToken}:{},...e.fetch?{fetch:e.fetch}:{}});throw new Error("No usable NodeTypesAdapter: runtime.nodes unavailable and no HTTP baseUrl")}async function Js(e){let t=await e.listNodeSets(),n=new Map;for(let r of t)for(let o of r.types){if(n.has(o))continue;let s={type:o,module:r.module,setId:r.id};typeof r.enabled=="boolean"&&(s.enabled=r.enabled),r.version&&(s.version=r.version),n.set(o,s)}return{byType:n,allNames:[...n.keys()]}}function Ap(e,t){let n=t.toLowerCase();return[e.type,e.module,e.category??"",e.description??""].join(`
|
|
4
|
+
`).toLowerCase().includes(n)}async function bo(e,t={}){let n=t.limit??cp,{byType:r}=await Js(e),o=[...r.values()].sort((u,p)=>u.type.localeCompare(p.type)),s=t.q?.trim();s&&(o=o.filter(u=>Ap(u,s)));let i=o.length,d=i>n,c={types:o.slice(0,n),total:i,truncated:d};return s&&(c.q=s),c}function xp(e,t){let n=`data-help-name="${t}"`,r=`data-help-name='${t}'`,o=e.toLowerCase(),s=0;for(;s<e.length;){let i=e.indexOf(n,s),d=e.indexOf(r,s),l=-1;if(i>=0&&(d<0||i<d)?l=i:d>=0&&(l=d),l<0)return null;let c=o.lastIndexOf("<script",l);if(c<0||c<s){s=l+1;continue}let u=e.indexOf(">",l);if(u<0)return null;let p=o.indexOf("</script",u+1);if(p<0)return null;let f=p+8;for(;f<e.length&&/\s/.test(e.charAt(f));)f+=1;if(e.charAt(f)!==">"){s=p+1;continue}return e.slice(u+1,p)}return null}function Ha(e,t){let n=`<${t}`,r=`</${t}`,o="",s=0,i=e.toLowerCase();for(;s<e.length;){let d=i.indexOf(n,s);if(d<0){o+=e.slice(s);break}o+=e.slice(s,d);let l=e.indexOf(">",d);if(l<0)break;let c=i.indexOf(r,l+1);if(c<0)break;let u=c+r.length;for(;u<e.length&&/\s/.test(e.charAt(u));)u+=1;e.charAt(u)===">"?(s=u+1,o+=" "):s=c+r.length}return o}function Sp(e,t=2e3){let n=Ha(e,"script");return n=Ha(n,"style"),n=n.replace(/<[^>]+>/g," ").replace(/ /gi," ").replace(/[#*_`]+/g," ").replace(/\s+/g," ").trim(),n.length>t&&(n=`${n.slice(0,t-1)}\u2026`),n}function Rp(e,t){try{let n=/RED\.nodes\.registerType\s*\(\s*['"]([^'"]+)['"]\s*,\s*\{/.exec(e);if(!n?.[1])return null;let r=n[1];if(t&&r!==t){let o=/RED\.nodes\.registerType\s*\(\s*['"]([^'"]+)['"]\s*,\s*\{/g,s,i=null,d=-1;for(;(s=o.exec(e))!==null;)if(s[1]===t){i=s[1],d=s.index;break}return!i||d<0?null:ja(e,d,i)}return ja(e,n.index,r)}catch{return null}}function ja(e,t,n){let r=e.slice(t),o=r.indexOf("{");if(o<0)return{type:n};let s=wr(r.slice(o));if(!s)return{type:n};let i=Ua(s,"category"),d=Ua(s,"color"),l=Ba(s,"inputs"),c=Ba(s,"outputs"),u=Pp(s),p=[];if(u){for(let g of Object.values(u))if(g&&typeof g=="object"&&!Array.isArray(g)){let w=g.type;typeof w=="string"&&w.length>0&&p.push(w)}}let f,h=xp(e,n);h&&(f=Sp(h));let m={type:n};return i&&(m.category=i),d&&(m.color=d),l!==void 0&&(m.inputs=l),c!==void 0&&(m.outputs=c),u&&(m.defaults=u),p.length>0&&(m.requiredConfigNodeTypes=[...new Set(p)]),f&&(m.help=f),m}function Tp(e){let t=e.trim();if(!t)return{};if(!/\n(?=#{1,3}\s*(inputs?|outputs?)\b)/i.test(`
|
|
5
|
+
${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(let l of r){let c=l.trim();if(!c)continue;let u=/^(#{1,3})\s*(inputs?|outputs?)\b[\s:]*\n?([\s\S]*)$/i.exec(c);if(u){let p=(u[2]??"").toLowerCase(),f=(u[3]??"").trim();p.startsWith("input")?s=f||c:i=f||c}else o||(o=c.replace(/^#{1,3}\s*/,"").trim())}let d={};return o&&(d.summary=o),s&&(d.inputs=s),i&&(d.outputs=i),!d.summary&&!d.inputs&&!d.outputs&&(d.text=t),d}function wr(e){if(!e.startsWith("{"))return null;let t=0,n=null,r=!1;for(let o=0;o<e.length;o++){let s=e[o];if(n){if(r){r=!1;continue}if(s==="\\"){r=!0;continue}s===n&&(n=null);continue}if(s==='"'||s==="'"||s==="`"){n=s;continue}if(s==="{")t+=1;else if(s==="}"&&(t-=1,t===0))return e.slice(0,o+1)}return null}function Ua(e,t){let n=new RegExp(`\\b${t}\\s*:\\s*['"]([^'"]+)['"]`);return e.match(n)?.[1]}function Ba(e,t){let n=new RegExp(`\\b${t}\\s*:\\s*(-?\\d+(?:\\.\\d+)?)`),r=e.match(n);if(!r?.[1])return;let o=Number(r[1]);return Number.isFinite(o)?o:void 0}function Pp(e){let t=e.search(/\bdefaults\s*:/);if(t<0)return;let n=e.slice(t),r=n.indexOf("{");if(r<0)return;let o=wr(n.slice(r));if(!o)return;let s={},i=/([a-zA-Z_$][\w$]*)\s*:\s*\{/g,d;for(;(d=i.exec(o))!==null;){let l=d[1];if(!l||l==="defaults")continue;let c=wr(o.slice(d.index+d[0].length-1));if(!c)continue;let u={},p=/\btype\s*:\s*['"]([^'"]+)['"]/.exec(c);p?.[1]&&(u.type=p[1]),/\brequired\s*:\s*true\b/.test(c)&&(u.required=!0);let f=/\bvalue\s*:\s*/.exec(c);if(f){let h=c.slice(f.index+f[0].length).trimStart();u.value=Ep(h)}s[l]=u}return Object.keys(s).length>0?s:void 0}function _p(e,t){let n=e.charAt(t);if(n==="u"&&/^[0-9a-fA-F]{4}/.test(e.slice(t+1,t+5)))return{char:String.fromCharCode(Number.parseInt(e.slice(t+1,t+5),16)),next:t+5};if(n==="x"&&/^[0-9a-fA-F]{2}/.test(e.slice(t+1,t+3)))return{char:String.fromCharCode(Number.parseInt(e.slice(t+1,t+3),16)),next:t+3};let r;switch(n){case"n":r=`
|
|
6
|
+
`;break;case"r":r="\r";break;case"t":r=" ";break;case"b":r="\b";break;case"f":r="\f";break;case"v":r="\v";break;case"0":r="\0";break;case"\\":r="\\";break;case"'":r="'";break;case'"':r='"';break;case"/":r="/";break;default:r=n;break}return{char:r,next:t+1}}function Cp(e){return e==="_DEFAULT_"?"":e}function wo(e){let t=e.trimStart();if(t.startsWith("'")||t.startsWith('"')){let r=t[0],o=1,s="";for(;o<t.length;){let i=t[o];if(i==="\\"&&o+1<t.length){let d=_p(t,o+1);s+=d.char,o=d.next;continue}if(i===r)break;s+=i,o+=1}return Cp(s)}if(t.startsWith("[")){let r=Ka(t);if(!r)return;let o=r.slice(1,-1).trim();if(!o)return[];let s=[],i=0;for(;i<o.length;){for(;i<o.length&&/[\s,]/.test(o.charAt(i));)i+=1;if(i>=o.length)break;let d=o.slice(i),l=wo(d);s.push(l);let c=Bs(d);i+=c>0?c:1}return s}if(t.startsWith("{")){let r=wr(t);if(!r)return;let o=r.slice(1,-1);if(!o.trim())return{};let s={},i=0;for(;i<o.length;){for(;i<o.length&&/[\s,]/.test(o.charAt(i));)i+=1;if(i>=o.length)break;let d;if(o[i]==="'"||o[i]==='"'){let c=wo(o.slice(i));d=typeof c=="string"?c:"",i+=Bs(o.slice(i))}else{let c=/^[a-zA-Z_$][\w$]*/.exec(o.slice(i));if(!c)break;d=c[0],i+=d.length}for(;i<o.length&&/\s/.test(o.charAt(i));)i+=1;if(o[i]!==":")break;for(i+=1;i<o.length&&/\s/.test(o.charAt(i));)i+=1;let l=o.slice(i);s[d]=wo(l),i+=Bs(l)}return s}let n=/^-?\d+(?:\.\d+)?/.exec(t);if(n)return Number(n[0]);if(t.startsWith("true"))return!0;if(t.startsWith("false"))return!1;if(t.startsWith("null"))return null}function Bs(e){let t=e.trimStart(),n=e.length-t.length;if(t.startsWith("'")||t.startsWith('"')){let o=t[0],s=1;for(;s<t.length;){if(t[s]==="\\"&&s+1<t.length){s+=2;continue}if(t[s]===o)return n+s+1;s+=1}return n+t.length}if(t.startsWith("[")){let o=Ka(t);return n+(o?.length??1)}if(t.startsWith("{")){let o=wr(t);return n+(o?.length??1)}let r=/^(?:-?\d+(?:\.\d+)?|true|false|null)/.exec(t);return r?n+r[0].length:n+1}function Ka(e){if(!e.startsWith("["))return null;let t=0,n=null,r=!1;for(let o=0;o<e.length;o++){let s=e[o];if(n){if(r){r=!1;continue}if(s==="\\"){r=!0;continue}s===n&&(n=null);continue}if(s==="'"||s==='"'){n=s;continue}if(s==="[")t+=1;else if(s==="]"&&(t-=1,t===0))return e.slice(0,o+1)}return null}function Ep(e){return wo(e)}function Ip(e,t){for(let n of e)if(n.types.includes(t)){let r=n.id.includes("/")||n.types.length===1?n.id:`${n.module}/${t}`;return{set:n,setId:r}}return null}function Vs(e,t){let n=e.toLowerCase();return t.includes(e)?e:t.find(r=>r.toLowerCase()===n)}async function Yt(e,t,n={}){if(!t||typeof t!="string")throw new x("Missing node type","missing_node_type");let r=n.includeHelp!==!1;if(zs(t)){let l={type:t,module:"node-red",category:"config",inputs:0,outputs:0,defaults:null};return r&&(l.help={summary:"Built-in structural Node-RED type (not a palette node)."}),l}let o=await e.listNodeSets(),s=o.flatMap(l=>l.types),i=Ip(o,t);if(!i){let l=Vs(t,s);throw l&&l!==t?new x(`Node type '${t}' was not found \u2014 did you mean '${l}'? Types are case-sensitive.`,"node_type_not_found",{statusCode:404,details:{type:t,didYouMean:l,caseSensitive:!0,nearest:[l]}}):new x(`Node type '${t}' was not found`,"node_type_not_found",{statusCode:404,details:{type:t,nearest:Gs(t,s,5)}})}let d={type:t,module:i.set.module,setId:i.setId,inputs:1,outputs:1,defaults:null};if(i.set.version&&(d.version=i.set.version),typeof i.set.enabled=="boolean"&&(d.enabled=i.set.enabled),typeof e.getNodeConfigHtml=="function")try{let l=await e.getNodeConfigHtml(i.setId)??await e.getNodeConfigHtml(`${i.set.module}/${t}`)??await e.getNodeConfigHtml(t);if(l){let c=Rp(l,t);c&&(c.category&&(d.category=c.category),c.color&&(d.color=c.color),d.inputs=c.inputs??d.inputs,d.outputs=c.outputs??d.outputs,c.defaults&&Object.keys(c.defaults).length>0?d.defaults=c.defaults:d.defaults=null,c.requiredConfigNodeTypes&&(d.requiredConfigNodeTypes=c.requiredConfigNodeTypes),r&&c.help&&(d.help=Tp(c.help)))}}catch{}return d}async function Ja(e,t){let{allNames:n}=await Js(e),r=new Set(n),o=new Set,s=a(l=>{let c=Ct(l,"node.type");if(o.has(c)||zs(c)){o.add(c);return}if(o.add(c),r.has(c))return;let u=Vs(c,n);throw u&&u!==c?new x(`Unknown node type '${c}' \u2014 did you mean '${u}'? Types are case-sensitive.`,"unknown_node_type",{details:{type:c,didYouMean:u,caseSensitive:!0,nearest:[u]}}):new x(`Unknown node type '${c}'. Call node-types / palette search; ask the user to install via Manage palette, then recheck.`,"unknown_node_type",{details:{type:c,nearest:Gs(c,n,5)}})},"checkType"),i=a(l=>{if(!l||typeof l!="object"||Array.isArray(l))return;let c=l;typeof c.id=="string"&&c.id.length>0&&s(c.type)},"visitNodeOrConfig");a(l=>{if(!l||typeof l!="object"||Array.isArray(l))return;let c=l;if(c.type!==void 0&&s(c.type),Array.isArray(c.nodes))for(let u of c.nodes)i(u);if(Array.isArray(c.configs))for(let u of c.configs)i(u)},"visitTab")(t)}async function Va(e,t){let n=Ct(t,"node.type");if(zs(n))return;let{allNames:r}=await Js(e);if(r.includes(n))return;let o=Vs(n,r);throw o&&o!==n?new x(`Unknown node type '${n}' \u2014 did you mean '${o}'? Types are case-sensitive.`,"unknown_node_type",{details:{type:n,didYouMean:o,caseSensitive:!0,nearest:[o]}}):new x(`Unknown node type '${n}'. Call node-types / palette search; ask the user to install via Manage palette, then recheck.`,"unknown_node_type",{details:{type:n,nearest:Gs(n,r,5)}})}async function Lp(e,t){let n=new Headers({Accept:"application/json"});t&&n.set("If-None-Match",t);let r=await fetch(e,{headers:n}),o=r.headers.get("etag")??void 0;if(r.status===304)return{status:304,...o?{etag:o}:{}};if(!r.ok)return{status:r.status,body:null,...o?{etag:o}:{}};let s=await r.json();return{status:r.status,body:s,...o?{etag:o}:{}}}async function Np(e,t){let n=Date.now();if(Be&&n-Be.fetchedAt<hp)return Be.body;let r=Be?.etag,o=await e(t,r);if(o.status===304&&Be)return Be={...Be,fetchedAt:n,...o.etag?{etag:o.etag}:{}},Be.body;if(!o.body||!Array.isArray(o.body.modules)){if(Be)return Be.body;throw new x("Node-RED catalogue unavailable","catalogue_unavailable",{statusCode:502})}return Be={fetchedAt:n,body:o.body,...o.etag?{etag:o.etag}:{}},o.body}async function vo(e,t,n={}){let r=t.trim();if(!r)throw new x("q query parameter is required","missing_query");let o=n.limit??up,s=n.catalogueUrl??fp,i=n.catalogueFetcher??Lp,d=await Np(i,s),l=Array.isArray(d.modules)?d.modules:[],c=await e.listNodeSets(),u=new Set(c.flatMap(g=>{let w=[g.module,g.id];return g.id.includes("/")&&w.push(g.id.split("/")[0]??g.module),w.map(v=>v.toLowerCase())})),p=r.toLowerCase(),f=[];for(let g of l){let w=g.id.toLowerCase(),v=(g.keywords??[]).map(O=>String(O).toLowerCase()),k=(g.description??"").toLowerCase(),$=Fp(p,w,v,k);if($<=0)continue;let C={module:g.id,installed:u.has(w)};typeof g.description=="string"&&(C.description=g.description),typeof g.version=="string"&&(C.version=g.version),typeof g.updated_at=="string"&&(C.updated=g.updated_at),typeof g.downloads=="number"&&(C.downloads=g.downloads),typeof g.url=="string"&&(C.link=g.url),f.push({item:C,score:$})}f.sort((g,w)=>w.score-g.score||(w.item.downloads??0)-(g.item.downloads??0)||g.item.module.localeCompare(w.item.module));let h=f.length,m=h>o;return{q:r,modules:f.slice(0,o).map(g=>g.item),total:h,truncated:m,hint:pp}}function Fp(e,t,n,r){if(!e)return 0;if(t===e||t===`node-red-contrib-${e}`||t===`node-red-node-${e}`)return 1e4;let o=0;t.startsWith(`node-red-contrib-${e}`)||t.startsWith(`node-red-node-${e}`)?o+=500:t.startsWith(e)?o+=80:t.includes(e)&&(o+=60);for(let s of n)s===e?o+=50:s.startsWith(e)||e.startsWith(s)?o+=35:s.includes(e)&&(o+=25);return r.includes(e)&&(o+=10),o}var qa,lp,cp,up,pp,fp,hp,mp,wp,qs,Da,Ws,Be,Zt=W(()=>{"use strict";y();qa=require("module");le();lp=new Set(["tab","subflow","group","junction"]),cp=100,up=30,pp="Recommend only \u2014 ask the user to install via Manage palette, then recheck node-types. This API never installs. Core Node-RED nodes (module node-red) are not listed in the public catalogue; use node-types for installed core types.",fp="https://catalogue.nodered.org/catalogue.json",hp=3600*1e3,mp=["inject","debug","function","template","comment","change","switch","delay","trigger","link in","link out","link call","mqtt in","mqtt out","http in","http response","http request","websocket in","websocket out","tcp in","tcp out","udp in","udp out","catch","status","complete","join","split","sort","batch","csv","html","json","xml","yaml","file","file in","watch","exec","range","rbe"];a(zs,"isStructuralNodeType");a(Ct,"requireNodeTypeName");a(gp,"editDistance");a(yp,"sharedPrefixLength");wp=new Set(["unknown","tab","subflow","group"]);a(bp,"nearestDistanceCutoff");a(Gs,"nearestNodeTypes");a(vp,"trimTrailingSlash");a(Wa,"createPluginRequire");a(kp,"isRuntimeSettingsAvailable");a(za,"normalizeNodeSet");a(Ks,"createStaticNodeTypesAdapter");qs=class{static{a(this,"HttpNodeTypesAdapter")}baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=vp(t.baseUrl),this.accessToken=t.accessToken,this.fetchImpl=t.fetch??globalThis.fetch.bind(globalThis)}headers(t){let n=new Headers;return n.set("Accept",t),this.accessToken&&n.set("Authorization",`Bearer ${this.accessToken}`),n}async listNodeSets(){let t=await this.fetchImpl(`${this.baseUrl}/nodes`,{headers:this.headers("application/json")});if(!t.ok)throw new Error(`Admin API GET /nodes failed (${t.status})`);let n=await t.json();if(!Array.isArray(n))throw new Error("Admin API GET /nodes returned non-array JSON");let r=[];for(let o of n){if(!o||typeof o!="object"||Array.isArray(o))continue;let s=za(o);s&&r.push(s)}return r}async getNodeConfigHtml(t,n){let r=t.split("/").map(i=>encodeURIComponent(i)).join("/"),o=n?`?lang=${encodeURIComponent(n)}`:"",s=await this.fetchImpl(`${this.baseUrl}/nodes/${r}${o}`,{headers:this.headers("text/html")});return s.status===404||!s.ok?null:s.text()}},Da={user:null,req:null},Ws=class{constructor(t){this.nodes=t}static{a(this,"RedNodeTypesAdapter")}async listNodeSets(){if(typeof this.nodes.getNodeList!="function")return[];let t,n=this.nodes.getNodeList;try{t=await Promise.resolve(n(Da))}catch{try{t=await Promise.resolve(n())}catch{return[]}}if(!Array.isArray(t))return[];let r=[];for(let o of t){if(!o||typeof o!="object"||Array.isArray(o))continue;let s=za(o);s&&r.push(s)}return r}async getNodeConfigHtml(t,n){if(typeof this.nodes.getNodeConfig!="function")return null;try{let r=await Promise.resolve(this.nodes.getNodeConfig({id:t,lang:n??"en-US",...Da}));if(typeof r=="string"&&r.length>0)return r}catch{}try{let r=await Promise.resolve(this.nodes.getNodeConfig(t,n));return typeof r=="string"&&r.length>0?r:null}catch{return null}}};a(Ga,"createNodeTypesAdapter");a(Js,"flattenInstalledTypes");a(Ap,"matchesQuery");a(bo,"listNodeTypes");a(xp,"extractHelpScriptBody");a(Ha,"removeHtmlBlocks");a(Sp,"stripHelpText");a(Rp,"parseRegisterTypeFromHtml");a(ja,"parseRegisterTypeAt");a(Tp,"splitHelpSections");a(wr,"extractBalancedObject");a(Ua,"matchStringProp");a(Ba,"matchNumberProp");a(Pp,"parseDefaultsBlock");a(_p,"unescapeJsStringEscape");a(Cp,"normalizeDefaultValue");a(wo,"parseJsLiteral");a(Bs,"measureJsLiteralLength");a(Ka,"extractBalancedArray");a(Ep,"parseLooseValue");a(Ip,"findSetForType");a(Vs,"findCaseInsensitiveType");a(Yt,"getNodeTypeDetail");a(Ja,"assertKnownNodeTypes");a(Va,"assertKnownNodeType");Be=null;a(Lp,"defaultCatalogueFetcher");a(Np,"loadCatalogue");a(vo,"searchPalette");a(Fp,"paletteMatchScore")});function gt(e){if(e.length===0)return;let t=e[0];if(!t)return;let n=e.map(s=>{let i={...s};return delete i.statusCode,i});if(e.length>1)throw new x(`${e.length} validation errors`,"validation_failed",{statusCode:400,details:{errors:n,errorCount:e.length}});let r={errors:n};for(let[s,i]of Object.entries(t))s==="code"||s==="error"||s==="errors"||s==="statusCode"||i!==void 0&&(r[s]=i);let o=typeof t.statusCode=="number"?t.statusCode:400;throw new x(t.error,t.code,{statusCode:o,details:r})}function Ys(e,t="payload.flow"){let n=new Map;for(let o of e)for(let s of o){if(!s||typeof s!="object"||Array.isArray(s))continue;let i=s.id;typeof i=="string"&&i.length>0&&n.set(i,(n.get(i)??0)+1)}let r=[];for(let[o,s]of n)s>1&&r.push({code:"duplicate_node_id",error:`Node id '${o}' appears ${s} times in ${t}; node ids must be unique`,nodeId:o,count:s});return r}function Op(e){let t=typeof e.label=="string"&&e.label?` '${e.label}'`:"";return e.type==="tab"?`tab${t||` '${e.id}'`}`:e.type==="subflow"?`subflow${t||` '${e.id}'`}`:typeof e.z=="string"&&e.z?`${e.type} node on '${e.z}'`:`global ${e.type} config node`}function Za(e,t,n){let r=new Map;for(let i of n)!i||typeof i.id!="string"||i.id===t||i.z===t||r.set(i.id,i);let o=[],s=new Set;for(let i of[e.nodes,e.configs])if(Array.isArray(i))for(let d of i){let l=d&&typeof d=="object"?d.id:void 0;if(typeof l!="string"||!l||s.has(l))continue;if(s.add(l),l===t){o.push({code:"duplicate_node_id",error:`Node id '${l}' equals the tab id; node ids must be unique across all flows`,nodeId:l});continue}let c=r.get(l);c&&o.push({code:"duplicate_node_id",error:`Node id '${l}' is already used by ${Op(c)}; node ids must be unique across all flows`,nodeId:l})}return o}function Ya(e){let t=new Map;for(let n of[e.nodes,e.configs])if(Array.isArray(n))for(let r of n){if(!r||typeof r!="object"||Array.isArray(r))continue;let o=r;typeof o.id=="string"&&t.set(o.id,o)}return t}function Mp(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]);for(let r of n)if(!$p.has(r)&&JSON.stringify(e[r])!==JSON.stringify(t[r]))return!0;return!1}function Dp(e,t){let n=Ya(t);if(!e)return new Set(n.keys());let r=Ya(e),o=new Set;for(let[s,i]of n){let d=r.get(s);(!d||Mp(d,i))&&o.add(s)}return o}function Xa(e,t){let n=[],r=e;typeof r.id!="string"||r.id.length===0?n.push({code:"missing_flow_id",error:"payload.flow.id is required and must match payload.flowId"}):r.id!==t&&n.push({code:"invalid_flow_id",error:`payload.flow.id ('${r.id}') must match payload.flowId ('${t}')`}),"nodes"in r?Array.isArray(r.nodes)||n.push({code:"invalid_nodes",error:"payload.flow.nodes must be an array"}):n.push({code:"invalid_nodes",error:"payload.flow.nodes must be an array (use [] to clear the tab intentionally)"}),"configs"in r&&r.configs!==void 0&&!Array.isArray(r.configs)&&n.push({code:"invalid_nodes",error:"payload.flow.configs must be an array"});let o=[];Array.isArray(r.nodes)&&o.push({key:"nodes",list:r.nodes}),Array.isArray(r.configs)&&o.push({key:"configs",list:r.configs}),n.push(...Ys(o.map(s=>s.list)));for(let{key:s,list:i}of o)for(let d=0;d<i.length;d+=1){let l=i[d];if(!l||typeof l!="object"||Array.isArray(l)){n.push({code:"invalid_node",error:`payload.flow.${s}[${d}] must be an object`});continue}let c=l;(typeof c.id!="string"||c.id.length===0)&&n.push({code:"missing_node_id",error:`payload.flow.${s}[${d}] needs a non-empty string id`});try{Ct(c.type,"node.type")}catch(u){u instanceof x&&n.push({code:u.code,error:u.message,...typeof c.id=="string"?{nodeId:c.id}:{}})}"wires"in c&&Cn(c.wires,n,typeof c.id=="string"?c.id:void 0)}return n}function Cn(e,t,n){if(!Array.isArray(e))return t.push({code:"invalid_wires",error:"wires must be an array of output ports (each a list of node ids)",...n?{nodeId:n}:{}}),!1;for(let r of e){if(!Array.isArray(r))return t.push({code:"invalid_wires",error:"wires must be an array of output ports (each a list of node ids)",...n?{nodeId:n}:{}}),!1;for(let o of r)if(typeof o!="string"||o.length===0)return t.push({code:"invalid_wires",error:"wires targets must be non-empty node id strings",...n?{nodeId:n}:{}}),!1}return!0}async function Hp(e,t){if(typeof e.outputs=="number"&&Number.isFinite(e.outputs)&&e.outputs>=0)return Math.trunc(e.outputs);let n=typeof e.type=="string"?e.type:"";if(!(!t||!n))try{return(await Yt(t,n,{includeHelp:!1})).outputs}catch{return}}function jp(e,t,n){let r=typeof e.type=="string"?e.type:"node";if(!(n.length<=t))return t===0?{code:"invalid_wires",error:`${r} has 0 outputs; wires must be []`,nodeId:e.id,type:r}:{code:"invalid_wires",error:`${r} has ${t} outputs; wires has ${n.length} port arrays (max ${t})`,nodeId:e.id,type:r}}function Up(e){return Array.isArray(e)&&e.some(t=>typeof t=="string"&&t.length>0)}function Bp(e,t){return t<=0?!1:!("wires"in e)||e.wires===void 0?!0:Array.isArray(e.wires)?!e.wires.some(Up):!1}async function Zs(e,t,n={}){let r=[],o=[],s=new Set(e.map(d=>d.id)),i=n.softWarningNodeIds;for(let d of e){let l=await Hp(d,t),c=typeof d.type=="string"?d.type:"node",u=!i||i.has(d.id);if("wires"in d&&d.wires!==void 0){if(!Cn(d.wires,r,d.id))continue;let p=d.wires;if(l!==void 0){let f=jp(d,l,p);f?(f.cause="excess_ports",r.push(f)):u&&p.length<l&&o.push({code:"incomplete_wires",error:`${c} '${d.id}' declares ${l} outputs but wires has only ${p.length} port(s)`,nodeId:d.id,type:c})}for(let f of p)for(let h of f){let m=String(h);s.has(m)||r.push({code:"invalid_wires",error:`Wire target '${m}' on node '${d.id}' was not found in this flow (add the node in the same patch or use an existing id)`,nodeId:d.id,cause:"missing_target",targetId:m})}}u&&l!==void 0&&Bp(d,l)&&o.push({code:"unwired_node",error:`Node '${d.id}' (${c}) has ${l} output(s) but is not wired to anything yet`,nodeId:d.id,type:c})}return{errors:r,warnings:o}}function Xs(e,t){let n=[];for(let r of e)r.z===void 0||r.z===""||typeof r.z=="string"&&r.z!==t&&n.push({code:"z_mismatch",error:`Node '${r.id}' has z='${r.z}' but flow id is '${t}'`,nodeId:r.id});return n}function qp(e,t){return e.filter(n=>n.type===t).map(n=>n.id)}async function Qs(e,t,n){let r=[],o=[...t];for(let s of e){let i;try{i=Ct(s.type,"node.type")}catch{continue}let d;try{d=await Yt(n,i,{includeHelp:!1})}catch{continue}let l=d.defaults;if(!l||typeof l!="object")continue;let c=new Set(d.requiredConfigNodeTypes??[]);for(let[u,p]of Object.entries(l)){if(!p||typeof p!="object"||Array.isArray(p))continue;let f=p.type;if(typeof f!="string"||!f)continue;let m=p.required===!0||c.has(f),g=s[u],w=qp(o,f);if(typeof g!="string"||g.length===0){m&&r.push({code:"missing_config_node",error:`Node '${s.id}' (${i}) has no ${f} on '${u}' yet \u2014 configure before deploy if needed`,nodeId:s.id,type:i,property:u,configType:f,candidates:w});continue}o.find(k=>k.id===g)?.type!==f&&r.push({code:"missing_config_node",error:`Node '${s.id}' property '${u}' does not resolve to a ${f} config yet`,nodeId:s.id,type:i,property:u,configType:f,candidates:w})}}return r}async function Qa(e,t={}){let n=t.flowId??(typeof e.id=="string"?e.id:""),r=Array.isArray(e.nodes)?e.nodes:[],o=Array.isArray(e.configs)?e.configs:[],s=[],i=[];n&&s.push(...Xs(r,n));let d=t.baseFlow?Dp(t.baseFlow,e):void 0,l=await Zs(r,t.nodeTypes,{...d?{softWarningNodeIds:d}:{}});if(s.push(...l.errors),i.push(...l.warnings),t.nodeTypes){let c=await Qs(r,o,t.nodeTypes);d?i.push(...c.filter(u=>!u.nodeId||d.has(u.nodeId))):i.push(...c)}return{errors:s,warnings:i}}var $p,ei=W(()=>{"use strict";y();le();Zt();a(gt,"throwValidationIssues");a(Ys,"collectDuplicateNodeIdIssues");a(Op,"describeLiveNode");a(Za,"collectCrossFlowDuplicateIssues");$p=new Set(["x","y","w","h","g","id","z"]);a(Ya,"collectNodesMap");a(Mp,"nodeContentChanged");a(Dp,"changedNodeIds");a(Xa,"collectFullFlowShapeIssues");a(Cn,"assertValidWiresShape");a(Hp,"resolveNodeOutputCount");a(jp,"wireCountIssue");a(Up,"portHasTargets");a(Bp,"nodeIsUnwired");a(Zs,"collectWireValidation");a(Xs,"collectZMismatchIssues");a(qp,"configCandidates");a(Qs,"collectConfigWarnings");a(Qa,"validateFlowTab")});function Et(e,t="node"){return typeof e=="string"&&e.trim()?e.trim():t}function ko(e){let t=new Map;for(let n of e)n.type==="tab"&&typeof n.id=="string"&&typeof n.label=="string"&&n.label.trim()&&t.set(n.id,n.label.trim());return t}function td(e,t){e[ed]=t}function Gp(e){if(!e)return;let t=e[ed];return t instanceof Map?t:void 0}function Kp(e){return typeof e!="string"||!e.trim()?new Set:new Set(e.split(",").map(t=>t.trim()).filter(Boolean))}function Jp(e){if(e===!0||e===1)return!0;if(typeof e=="string"){let t=e.trim().toLowerCase();return t==="1"||t==="true"||t==="yes"}return!1}function ve(e){let t=Kp(e.include);return Jp(e.include_payload)&&(t.add("payload"),t.add("baseFlow")),t}function Z(e,t){let n={req:e};t!==void 0&&(n.httpAdminRoot=t);let r=Gp(e);return r&&(n.liveFlowLabels=r),n}function En(e){return typeof e.name=="string"&&e.name.trim()?e.name.trim():typeof e.label=="string"&&e.label.trim()?e.label.trim():""}function br(e){return`"${e.replace(/"/g,"'")}"`}function In(e){let t=new Map;for(let n of e.nodes??[])t.set(n.id,n);for(let n of e.configs??[])t.set(n.id,n);return t}function Vp(e,t){let n=new Set(["x","y","w","h","g","id","z"]),r=new Set([...Object.keys(e),...Object.keys(t)]);for(let o of r)if(!n.has(o)&&JSON.stringify(e[o])!==JSON.stringify(t[o]))return!0;return!1}function Yp(e,t){let n=En(e),r=En(t);if(!n||!r||n===r)return!1;let o=new Set(["x","y","w","h","g","id","z","name","label"]),s=new Set([...Object.keys(e),...Object.keys(t)]);for(let i of s)if(!o.has(i)&&JSON.stringify(e[i])!==JSON.stringify(t[i]))return!1;return!0}function ti(e,t){let n=[],r=[],o=[];for(let[s,i]of t)e.has(s)||n.push(i);for(let[s,i]of e)t.has(s)||r.push(i);for(let[s,i]of t){let d=e.get(s);d&&Vp(d,i)&&o.push({before:d,after:i})}return{added:n,removed:r,modified:o}}function Ao(e){return e===1?"node":"nodes"}function Zp(e){return e.length===1&&e[0]?`adds 1 ${Et(e[0].type)}`:`adds ${e.length} ${Ao(e.length)}`}function Xp(e){return`deletes ${e} ${Ao(e)}`}function Qp(e){return`edits ${e} ${Ao(e)}`}function ef(e){let t=[];e.removed>0&&t.push(Xp(e.removed));let n=e.added??[],r=e.addedCount??n.length;r>0&&t.push(n.length===r?Zp(n):`adds ${r} ${Ao(r)}`),(e.modified??0)>0&&t.push(Qp(e.modified??0));let o=t.length?t.join(", "):"replaces nodes",s=e.flowLabel?.trim();return s?`Replace ${s}: ${o}`:`Replace flow: ${o}`}function tf(e){let{added:t,removed:n,modified:r}=e;if(t.length+n.length+r.length!==1)return;if(t[0]){let u=t[0],p=Et(u.type),f=En(u);return f?`Add ${p} node ${br(f)}`:`Add ${p} node`}if(n[0]){let u=n[0],p=Et(u.type),f=En(u);return f?`Remove ${p} node ${br(f)}`:`Remove ${p} node`}let s=r[0];if(!s)return;let i=Et(s.after.type,Et(s.before.type)),d=En(s.before),l=En(s.after);if(Yp(s.before,s.after)&&d&&l)return`Rename ${i} node ${br(d)} \u2192 ${br(l)}`;let c=l||d;return c?`Edit ${i} ${br(c)}`:`Edit ${i}`}function ni(e,t){return typeof e?.label=="string"&&e.label.trim()?e.label.trim():typeof t?.label=="string"&&t.label.trim()?t.label.trim():""}function xo(e,t){if(typeof e.flowLabel=="string"&&e.flowLabel.trim())return e.flowLabel.trim();let n=ni(e.payload?.flow,e.baseFlow);if(n)return n;let r=e.payload?.flowId;return r&&t?.has(r)?t.get(r)??"":""}function nd(e,t){let n=t.trim(),r=e.trim();if(!n||!r||r.includes(n))return r;let o=r.lastIndexOf(" in ");if(o>=0){let s=r.slice(o+4);if(s.length>0&&n.startsWith(s))return r}return`${r} in ${n}`}function ri(e,t){let n=ti(In(e??{}),In(t)),r=n.added.length+n.removed.length+n.modified.length,o=ni(t,e);if(r===0)return o?`Update ${o}`:"Update flow";if(n.removed.length>0)return ef({...o?{flowLabel:o}:{},removed:n.removed.length,added:n.added,modified:n.modified.length});if(r>zp)return o?`${r} changes in ${o}`:`${r} changes`;let s=tf(n)??(o?`Update ${o}`:"Update flow");return nd(s,o)}function nf(e,t){let n=(e??"").trim();return!!(!n||n.includes("[object Object]")||/^Update(\s|$)/i.test(n)||t>0&&!/^Replace\b/i.test(n))}function oi(e,t){return`${e} \xB7 ${of(t)}`}function rf(e){let t=e>=0?"+":"-",n=Math.abs(e),r=String(Math.floor(n/60)).padStart(2,"0"),o=String(n%60).padStart(2,"0");return`${t}${r}:${o}`}function of(e,t=new Date(e)){let n=Number.isNaN(t.getTime())?new Date(e):t;if(Number.isNaN(n.getTime()))return e.slice(0,16);let r=n.getDate(),s=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"][n.getMonth()]??"Jan",i=String(n.getHours()).padStart(2,"0"),d=String(n.getMinutes()).padStart(2,"0"),l=rf(-n.getTimezoneOffset());return`${r} ${s} ${i}:${d} ${l}`}function vr(e){if(typeof e!="string")return;let t=e.replace(/\0/g,"").replace(/[\u0001-\u001F\u007F]/g," ").replace(/\s+/g," ").trim().slice(0,Wp).trim();return t.length>0?t:void 0}function sf(e,t){let n=Pe(t);return`${n==="/"?"/ai-access":`${n}/ai-access`}#proposal=${encodeURIComponent(e)}`}function si(e,t={}){let n=xo(e,t.liveFlowLabels),r=vr(e.title),o=ri(e.baseFlow,n&&!ni(e.payload.flow,e.baseFlow)?{...e.payload.flow,label:n}:e.payload.flow),s=ti(In(e.baseFlow??{}),In(e.payload.flow)),i=nf(r,s.removed.length)?o:r??o;return nd(i,n)}function af(e){return typeof e.storedTitle=="string"&&e.storedTitle?e.storedTitle:vr(e.title)}function ce(e,t={}){let n=si(e,t),r=xo(e,t.liveFlowLabels),o=af(e)??n,s=sf(e.id,t.httpAdminRoot),i=t.req?mr(t.req,s):s;return{...e,...e.conflictChoice?{conflictChoice:{nodeId:e.conflictChoice.nodeId,choice:e.conflictChoice.choice,...e.conflictChoice.chooser?{chooser:e.conflictChoice.chooser}:{}}}:{},title:n,storedTitle:o,flowLabel:r,displayName:oi(n,e.createdAt),reviewUrl:i,reviewPath:s}}function df(e){let t=ti(In(e.baseFlow??{}),In(e.payload.flow)),n=[];return t.added.length>0&&n.push(t.added.length===1?"Adds 1 node":`Adds ${t.added.length} nodes`),t.removed.length>0&&n.push(t.removed.length===1?"Deletes 1 node":`Deletes ${t.removed.length} nodes`),t.modified.length>0&&n.push(t.modified.length===1?"Edits 1 node":`Edits ${t.modified.length} nodes`),n.length===0?"No node changes":n.map((r,o)=>o===0?r:r.charAt(0).toLowerCase()+r.slice(1)).join(", ")}function lf(e){if(e.kind==="oauth")return{kind:"oauth",clientId:e.clientId,user:e.user};let t={kind:e.kind,linkId:e.linkId};return typeof e.label=="string"&&e.label.length>0&&(t.label=e.label),t}function ue(e,t={}){let n=ce(e,t),r=t.include??new Set,o={id:n.id,status:n.status,type:n.type,title:n.title,storedTitle:n.storedTitle,displayName:n.displayName,flowLabel:n.flowLabel,reviewUrl:n.reviewUrl,reviewPath:n.reviewPath,createdAt:n.createdAt,updatedAt:n.updatedAt,expectedRev:n.expectedRev,baseRev:n.expectedRev,createdBy:lf(n.createdBy),changeSummary:df(e)};return r.has("payload")&&(o.payload=n.payload),r.has("baseFlow")&&n.baseFlow!==void 0&&(o.baseFlow=n.baseFlow),n.warnings&&n.warnings.length>0&&(o.warnings=n.warnings),typeof e.payload.flowId=="string"&&(o.flowId=e.payload.flowId),e.restoredSecrets&&e.restoredSecrets.length>0&&(o.restoredSecrets=e.restoredSecrets),n.deploymentAlert&&(o.deploymentAlert=n.deploymentAlert),n.rejectedWhileDeployUnknown&&(o.rejectedWhileDeployUnknown=!0),n.deployPending&&(o.deployPending=n.deployPending),n.aiConflictOverrides&&(o.aiConflictOverrides=n.aiConflictOverrides),n.conflictChoice&&(o.conflictChoice=n.conflictChoice),n.rebase&&(o.rebase=n.rebase),n.rebase&&(n.status==="pending"||n.status==="conflict")&&(o.baseRevChanged=n.expectedRev!==n.rebase.rev),n.baseRevChanged!==void 0&&(o.baseRevChanged=n.baseRevChanged),n.decidedBy!==void 0&&(o.decidedBy=n.decidedBy),n.reason!==void 0&&(o.reason=n.reason),n.resultRev!==void 0&&(o.resultRev=n.resultRev),o}function rd(e,t={}){return e.map(n=>ue(n,t))}var Wp,zp,ed,qe=W(()=>{"use strict";y();Re();Ye();Wp=80,zp=1;a(Et,"nodeTypeText");ed=Symbol.for("ai-access.liveFlowLabels");a(ko,"liveFlowLabelsFromFlows");a(td,"setRequestFlowLabels");a(Gp,"getRequestFlowLabels");a(Kp,"parseIncludeQuery");a(Jp,"isTruthyFlag");a(ve,"resolveIncludeFromQuery");a(Z,"presentOptionsFor");a(En,"nodeLabel");a(br,"quoteName");a(In,"collectNodes");a(Vp,"nodeContentChanged");a(Yp,"isRenameOnly");a(ti,"classifyFlowChanges");a(Ao,"pluralNodes");a(Zp,"describeAddedClause");a(Xp,"describeRemovedClause");a(Qp,"describeModifiedClause");a(ef,"buildReplaceFlowTitle");a(tf,"describeOneChange");a(ni,"flowLabelHint");a(xo,"resolveFlowLabel");a(nd,"ensureTitleIncludesFlowName");a(ri,"autoGenerateProposalTitle");a(nf,"shouldRefreshDisplayTitle");a(oi,"buildProposalDisplayName");a(rf,"formatUtcOffset");a(of,"formatProposalSubmittedAt");a(vr,"sanitizeProposalTitle");a(sf,"buildProposalReviewPath");a(si,"resolveProposalTitle");a(af,"storedTitleOf");a(ce,"presentProposal");a(df,"buildChangeSummary");a(lf,"presentCreatedByForApi");a(ue,"presentProposalForApi");a(rd,"presentProposalsForApi")});function Rr(e){let t=e.join("\0"),n=So.get(t);if(n)return n;let r=[...new Set(e.map(d=>Yr(d).join("")).filter(Boolean))],o=new Map,s={patterns:[...e],matches(d){if(typeof d!="string"||d.length===0||r.length===0)return!1;let l=o.get(d);if(l!==void 0)return l;let c=i(d);return o.size>4096&&o.clear(),o.set(d,c),c}};function i(d){{let l=Yr(d),c=l[l.length-1];if(!c||cf.has(c))return!1;for(let u of r){let p=`${u}S`;for(let f=0;f<l.length;f++){let h="";for(let g=f;g<l.length;g++){if(h+=l[g],h===u||h===p)return!0;if(h.length>p.length)break}let m=l[f]??"";if(u.length>=6&&(m.endsWith(u)||m.endsWith(p)))return!0}}return!1}}return a(i,"matchUncached"),So.size>32&&So.clear(),So.set(t,s),s}function It(){return va()?.getEffective()?.secretNamePatterns??Wt}function Ar(e){let t=e.trim();return!(!t||t===X||uf.has(t.toLowerCase())||/^\$\{[A-Za-z0-9_]+\}$/.test(t))}function ff(e){let t=id.get(e);return t||(t=hf(e),id.set(e,t)),t}function hf(e){return[{hints:["Buffer.from","btoa"],re:/\b(?:Buffer\.from|btoa)\([ \t]{0,8}(?<q>["'`])[^"'`:\s]{1,256}:(?<s>[^"'`\s]{1,1024})\k<q>/dg,accept:a(t=>!t.includes("${"),"accept")},{hints:["PRIVATE KEY-----"],re:/-----BEGIN ((?:[A-Z0-9]{1,16} ){0,4})PRIVATE KEY-----(?<s>[^-]{1,16384})-----END \1PRIVATE KEY-----/dg},{hints:["AKIA","ASIA","ABIA","ACCA"],re:/(?<![A-Za-z0-9])(?<s>(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16})(?![A-Za-z0-9])/dg},{hints:["ghp_","gho_","ghu_","ghs_","ghr_"],re:/(?<![A-Za-z0-9_])(?<s>gh[pousr]_[A-Za-z0-9]{36,255})(?![A-Za-z0-9_])/dg},{hints:["github_pat_"],re:/(?<![A-Za-z0-9_])(?<s>github_pat_[A-Za-z0-9_]{22,255})(?![A-Za-z0-9_])/dg},{hints:["xox"],re:/(?<![A-Za-z0-9_-])(?<s>xox[abposr]-[A-Za-z0-9-]{10,512})/dg},{hints:["hooks.slack.com"],re:/hooks\.slack\.com\/services\/(?<s>[A-Za-z0-9_/]{16,512})/dg},{hints:["sk-"],re:/(?<![A-Za-z0-9_-])(?<s>sk-[A-Za-z0-9_-]{20,512})/dg},{hints:["k_live_","k_test_"],re:/(?<![A-Za-z0-9_])(?<s>[sr]k_(?:live|test)_[A-Za-z0-9]{16,512})/dg},{hints:["AIza"],re:/(?<![A-Za-z0-9_-])(?<s>AIza[0-9A-Za-z_-]{35})(?![A-Za-z0-9_-])/dg},{hints:["eyJ"],re:/(?<![A-Za-z0-9_-])(?<s>eyJ[A-Za-z0-9_-]{5,8192}\.[A-Za-z0-9_-]{5,8192}\.[A-Za-z0-9_-]{5,8192})/dg},{hints:["earer"],re:/\b[Bb]earer[ \t]{1,8}(?<s>(?=[A-Za-z0-9\-._~+/]{0,4096}[0-9\-._~+/])[A-Za-z0-9\-._~+/]{16,4096}={0,3})/dg},{hints:["asic"],re:/\b[Bb]asic[ \t]{1,8}(?<s>(?=[A-Za-z0-9+/]{0,4096}[0-9+/])[A-Za-z0-9+/]{8,4096}={0,2})(?![A-Za-z0-9+/])/dg},{hints:["@"],re:/\b[A-Za-z][A-Za-z0-9+.-]{1,20}:\/\/[^\s:@/'"`<>]{1,256}:(?<s>[^\s@/'"`<>]{1,1024})@/dg,accept:a(t=>!/^(?:\$\{|\{\{|<|%)/.test(t)&&t!==X,"accept")},{hints:["?","&"],re:/[?&](?<k>[A-Za-z_][\w.-]{0,63})=(?<s>[^&#\s"'`<>]{1,2048})/dg,accept:a((t,n)=>e.matches(n.groups?.k??"")&&Ar(bf(t)),"accept")},{hints:[":","="],re:/(?<![\w$-])(?<q>["'`]?)(?<k>[A-Za-z_$][\w$-]{0,63})\k<q>\]?[ \t]{0,8}(?::|=(?![=>]))[ \t]{0,8}(?<vq>["'`])(?<s>(?:\\.|(?!\k<vq>)[^\\\r\n]){1,1024})\k<vq>/dg,accept:a((t,n)=>{let r=n.groups?.k??"";return!e.matches(r)||n.groups?.vq==="`"&&t.includes("${")||t.length<3||/\s/.test(t)?!1:Ar(t)},"accept")}]}function gf(e){let t=new Map;for(let r of e)t.set(r,(t.get(r)??0)+1);let n=0;for(let r of t.values()){let o=r/e.length;n-=o*Math.log2(o)}return n}function yf(e){return/[^A-Za-z]/.test(e)||e.length>=16?!0:[/[a-z]/,/[A-Z]/,/[0-9]/,/[^A-Za-z0-9]/].filter(n=>n.test(e)).length>=3||gf(e)>=3}function wf(e){let t=e.patterns.filter(r=>mf.includes(r.toUpperCase())),n=Rr(t);return{hints:[":","="],re:/(?<![\w$.-])(?<k>[A-Za-z_][\w-]{0,63})[ \t]{0,8}(?::(?!\/\/)|=)[ \t]{0,8}(?<s>[^\s;,'"`&<>(){}[\]]{3,1024})/dg,accept:a((r,o)=>e.matches(o.groups?.k??"")&&Ar(r)&&!r.startsWith("$")&&!r.startsWith("{{")&&(n.matches(o.groups?.k??"")||yf(r))&&!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/.test(r)&&o.input.charAt(o.indices?.groups?.s?.[1]??0)!=="(","accept")}}function bf(e){try{return decodeURIComponent(e)}catch{return e}}function vf(e,t,n){let r=e.indexOf("=",t);if(r<=t||r>=n||r-t>48)return;let o=e.slice(t,r),s=Math.max(o.lastIndexOf('"'),o.lastIndexOf("'"),o.lastIndexOf("`")),i=o.slice(s+1).trim();if(!/^[A-Za-z][\w -]{0,40}$/.test(i))return;let d=r+1;for(;d<n&&(e[d]===" "||e[d]===" ");)d++;let l=d;for(;l<n&&!"\"'`\r".includes(e[l]??"");)l++;for(;l>d&&(e[l-1]===" "||e[l-1]===" ");)l--;return{k:i,vs:d,ve:l}}function kf(e,t){if(!e.includes(";")||!e.includes("="))return[];let n=[],r=0;for(;r<=e.length;){let o=e.indexOf(`
|
|
7
|
+
`,r);o<0&&(o=e.length);let s=[],i=r,d=!1;for(;i<o;){let l=e.indexOf(";",i);l<0||l>o?l=o:d=!0;let c=vf(e,i,l);c&&s.push(c),i=l+1}if(d&&s.length>=2)for(let l of s)l.ve<=l.vs||!t.matches(l.k)||Ar(e.slice(l.vs,l.ve))&&n.push({start:l.vs,end:l.ve});r=o+1}return n}function Af(e,t,n={}){if(e.length===0)return{text:e,secrets:[],spans:[]};let r=[],o=ff(t);if(!n.code){let u=ad.get(t);u||(u=wf(t),ad.set(t,u)),o=[...o,u]}r.push(...kf(e,t));for(let u of o){if(u.hints&&!u.hints.some(f=>e.includes(f)))continue;u.re.lastIndex=0;let p;for(;(p=u.re.exec(e))!==null;){let f=p.indices?.groups?.s,h=p.groups?.s;f&&h!==void 0&&h.length>0&&(!u.accept||u.accept(h,p))&&r.push({start:f[0],end:f[1]}),p[0].length===0&&u.re.lastIndex++}}if(r.length===0)return{text:e,secrets:[],spans:[]};r.sort((u,p)=>u.start-p.start||p.end-u.end);let s=[];for(let u of r){let p=s[s.length-1];p&&u.start<p.end?p.end=Math.max(p.end,u.end):s.push({...u})}let i="",d=0,l=[],c=[];for(let u of s){let p=e.slice(u.start,u.end);p!==X&&(i+=e.slice(d,u.start)+X,l.push(p),c.push(u),d=u.end)}return i+=e.slice(d),{text:i,secrets:l,spans:c}}function li(e,t){let n=[],r=0,o=a(s=>{let i=_o(s);for(let d=0;d<i;d++)n.push({kind:"literal",value:X})},"literals");for(let s of t)o(e.slice(r,s.start)),n.push({kind:"secret",value:e.slice(s.start,s.end)}),r=s.end;return o(e.slice(r)),n}function oe(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function xf(e){return oe(e)&&typeof e.id=="string"&&typeof e.type=="string"}function ci(e,t){let n=new ai(t,e.id),r=e[kr];if(Array.isArray(r))for(let s of r)typeof s=="string"&&n.paths.push(s);let o={};for(let[s,i]of Object.entries(e))s!==kr&&(cd.has(s)?o[s]=i:fd.has(s)&&Array.isArray(i)?o[s]=i.map(d=>ui(d,t)):s==="env"&&Array.isArray(i)?o[s]=n.env(i,"env"):o[s]=n.value(i,s,s,e));return n.paths.length>0&&(o[kr]=n.paths),o}function ui(e,t){return Array.isArray(e)?e.map(n=>ui(n,t)):xf(e)?ci(e,t):e}function yt(e,t=It()){let n={matcher:Rr(t),count:0};return{value:ui(e,n),count:n.count}}function pi(e,t=It()){return e.map(n=>{if(n.nodeName===void 0)return{...n};let r=yt({id:"conflict",type:"node",name:n.nodeName},t);return{...n,nodeName:r.value.name}})}function nt(e,t=It()){let n={...e,payload:{...e.payload,flow:yt(e.payload.flow,t).value}};return e.conflictChoice&&(n.conflictChoice={nodeId:e.conflictChoice.nodeId,choice:e.conflictChoice.choice,...e.conflictChoice.chooser?{chooser:e.conflictChoice.chooser}:{}}),e.baseFlow!==void 0&&(n.baseFlow=yt(e.baseFlow,t).value),e.rebase&&(n.rebase={...e.rebase,conflicts:pi(e.rebase.conflicts,t)}),n}function Lt(e,t=It){if(hd(e))return e;let n=t===It,r=n?dd.get(e):void 0;if(r)return r;let o={kind:e.kind,[fi]:e,async getFlows(){let s=await e.getFlows();return{rev:s.rev,flows:yt(s.flows,t()).value}},async getFlow(s){let i=await e.getFlow(s);return{rev:i.rev,flow:yt(i.flow,t()).value}},updateFlow:a(s=>e.updateFlow(s),"updateFlow")};return n&&dd.set(e,o),o}function hd(e){return fi in e}function we(e){return hd(e)?e[fi]:e}function rt(e,t){return e.accessLink||e.auth?Lt(t):t}function _o(e){let t=0,n=e.indexOf(X);for(;n!==-1;)t++,n=e.indexOf(X,n+X.length);return t}function xr(e){let t=e.split(X),n=[];for(let r=1;r<t.length;r++){let o=t[r-1]??"",s=t[r]??"",i=o.lastIndexOf(`
|
|
8
8
|
`),d=s.indexOf(`
|
|
9
|
-
`);n.push({before:i>=0?o.slice(i+1):o,after:d>=0?s.slice(0,d):s})}return n}function go(e){return JSON.stringify([e.before,e.after])}function ho(e){let t=new Map;return ur(e.text).forEach((n,r)=>{let o=go(n),s=t.get(o)??[];s.push(r),t.set(o,s)}),t}function Qa(e,t,n,r){if(e===t)return{text:n,restored:r.filter(f=>f.kind==="secret").length};let o=ur(e),s=ur(t);if(o.length>s.length)return{error:`contains ${o.length} [REDACTED] placeholders but the original had ${s.length}`};let i=ho({text:t,occurrences:r}),d=new Set([...i].filter(([,f])=>new Set(f.map(h=>r[h]?.kind)).size>1).map(([f])=>f)),l=new Map,c=e.split(J),u=c[0]??"",p=0;for(let f=0;f<o.length;f++){let h=o[f];if(!h)break;let m=go(h),g=i.get(m)??[];if(d.has(m))return{error:"ambiguous literal and secret [REDACTED] occurrences"};let y=l.get(m)??0,A=g[y]??-1;if(A<0)return{error:"a [REDACTED] placeholder is not on an unchanged original line"};l.set(m,y+1);let x=r[A];x?.kind==="secret"&&p++,u+=(x?.value??J)+(c[f+1]??"")}return{text:u,restored:p}}function vt(e){if(Z(e))for(let t of sf){let n=e[t];if(typeof n=="string"&&n.length>0)return`${t}=${n}`}}function fr(e,t){let n=a(s=>[s,...s.nodes??[],...s.configs??[]],"entities"),r=new Map(n(e).map(s=>[s.id,s])),o=[];for(let s of n(t)){let i=r.get(s.id);if(JSON.stringify(i?.env)===JSON.stringify(s.env))continue;let d=a(l=>{let c=new Set;return Array.isArray(l)?l.some(u=>!Z(u)||typeof u.name!="string"||!u.name?!1:c.has(u.name)?!0:(c.add(u.name),!1)):!1},"hasDuplicates");!d(i?.env)&&!d(s.env)||o.push({code:"duplicate_env_keys",...s.id?{nodeId:s.id}:{},property:"env",error:"Duplicate env names make pairing by name ambiguous when entries are reordered. Review the env values before approval."})}return o}function Qs(e,t,n,r=[]){let o=a(c=>{let u=new Map;return yo(c,{matcher:kn(dt()),count:0,fields:u}),u},"fieldsFor"),s=o(e),i=o(t),d=o(n),l=a((c,u,p,f,h=f,m=f)=>{if(typeof u=="string"){let g=s.get(`${e.id}\0${f}`);if(!g?.occurrences.some(E=>E.kind==="secret"))return u;let y=i.get(`${t.id}\0${h}`),A=d.get(`${n.id}\0${m}`),x=ur(g.text),$=ho(g),L=y?ho(y):new Map,D=A?ho(A):new Map,H=y?.occurrences.map(E=>E.value)??[],T=y?ur(y.text):[],k=y?.occurrences.some((E,v)=>{let S=T[v];return E.kind==="secret"&&S&&!$.has(go(S))}),_=!1;return g.occurrences.forEach((E,v)=>{if(E.kind!=="secret")return;let S=x[v],N=S?go(S):"",w=$.get(N)??[],F=L.get(N)??[],I=D.get(N)??[];if(w.length>1&&F.length>0&&F.length<w.length&&JSON.stringify(w.map(Bn=>g.occurrences[Bn]?.value))!==JSON.stringify(I.map(Bn=>A?.occurrences[Bn]?.value)))throw new pr;let C=a((Bn,Jc)=>{let Gi=Bn.get(N)??[],[fs]=Gi;return Gi.length===1&&fs!==void 0&&Jc?.occurrences[fs]?.kind==="secret"?fs:void 0},"matches"),ee=C($,g),ot=C(L,y),Un=C(D,A);if(!L.has(N)&&!k)return;if(ee===void 0||ot===void 0||Un===void 0){_=!0;return}let Wi=A?.occurrences[Un];y?.occurrences[ot]?.value===E.value&&Wi&&(H[ot]=Wi.value)}),_&&r.push({code:"secret_may_revert",nodeId:e.id,property:h,error:"Secret spans could not be aligned reliably. The proposed value was kept and may revert an editor secret change. Review this field before approval."}),!y||_?u:y.text.split(J).reduce((E,v,S)=>E+(S?H[S-1]??J:"")+v,"")}if(Array.isArray(u)){let g=Array.isArray(c)?c:[],y=Array.isArray(p)?p:[],A=new Set,x=new Set;return u.map(($,L)=>{let D=vt($),H=a((_,E)=>{let v=D===void 0?L:_.findIndex((S,N)=>!E.has(N)&&vt(S)===D);return E.add(v),v},"match"),T=H(g,A),k=H(y,x);return l(g[T],$,y[k],`${f}[${T}]`,`${h}[${L}]`,`${m}[${k}]`)})}if(Z(u)){let g=Z(c)?c:{},y=Z(p)?p:{},A=a((x,$)=>x?`${x}.${$}`:$,"path");return Object.fromEntries(Object.entries(u).map(([x,$])=>[x,l(g[x],$,y[x],A(f,x),A(h,x),A(m,x))]).filter(([,x])=>x!==void 0))}return u},"walk");return l(e,t,n,"")}function ed(e,t){let n=Vs(t,[]);return"error"in Qa(e,t,t,n)}function mo(e,t,n,r,o,s){if(typeof e=="string"){if(!e.includes(J))return e;let i=s.fields.get(`${r}\0${o}`);if(typeof t=="string"&&typeof n=="string"&&(i||n.includes(J))){let d=Qa(e,t,n,i?.occurrences??Vs(n,[]));return"error"in d?(s.unresolved.push({nodeId:r,property:o,reason:d.error}),e):(d.restored>0&&(s.restored+=d.restored,s.restoredFields.push({nodeId:r,property:o})),d.text)}return s.unresolved.push({nodeId:r,property:o,reason:"no redacted secret exists at this node id and field"}),e}if(Array.isArray(e)){let i=Array.isArray(t)?t:[],d=Array.isArray(n)?n:[],l=new Set;return e.map(c=>{let u=Z(c)&&c.redacted===!0?Js(c,"redacted"):c,p=vt(u),f=-1;if(p!==void 0){let h=d.map((m,g)=>vt(m)===p&&!l.has(g)?g:-1).filter(m=>m>=0);h.length>0&&(f=h[0]??-1)}return f>=0?(l.add(f),mo(u,i[f],d[f],r,`${o}[${f}]`,s)):mo(u,void 0,void 0,r,`${o}[?]`,s)})}if(Z(e)){let i=Z(t)?t:{},d=Z(n)?n:{},l={};for(let[c,u]of Object.entries(e))l[c]=mo(u,i[c],d[c],r,o?`${o}.${c}`:c,s);return l}return e}function Js(e,t){let n={...e};return delete n[t],n}function af(e){let t=Js(e,dr);return Array.isArray(t.env)&&(t.env=t.env.map(n=>!Z(n)||n.redacted!==!0?n:Js(n,"redacted"))),t}function td(e,t,n,r){let o=typeof e.id=="string"?e.id:"",s=o?t.get(o):void 0,i={},d=s?yo({...s,id:o},r):void 0;for(let[l,c]of Object.entries(e))if(l!==dr){if(Ya.has(l)&&Array.isArray(c)){i[l]=c.map(u=>Z(u)?td(u,t,n,r):u);continue}if(Ka.has(l)){i[l]=c;continue}i[l]=mo(c,d?.[l],s?.[l],o,l,n)}return af(i)}function nd(e,t,n=dt()){let r=new Map;for(let l of t)Z(l)&&typeof l.id=="string"&&r.set(l.id,l);let o=new Map,s={matcher:kn(n),count:0,fields:o},i={fields:o,restored:0,restoredFields:[],unresolved:[]};return{flow:td(e,r,i,s),restored:i.restored,restoredFields:i.restoredFields,unresolved:i.unresolved}}function uf(e){return typeof e.type=="string"&&lf.has(e.type)?!0:cf.some(t=>typeof e[t]=="string"&&e[t]!=="")}function Ga(e){let t=new Set,n=a((r,o)=>{if(!(o>12)){if(typeof r=="string"){if(r.length>Va)return;let s=/(?:\b(?:env|global|flow)\.get|\$env)\([ \t]{0,8}["'`]([^"'`\r\n]{1,128})["'`]|\$\{([A-Za-z_][\w.-]{0,127})\}/g,i;for(;(i=s.exec(r))!==null;)t.add(i[1]??i[2]??"");return}if(Array.isArray(r)){for(let s of r)n(s,o+1);return}if(Z(r))for(let[s,i]of Object.entries(r))[`${s}Type`,s==="v"?"vt":"",s==="to"?"tot":"",s==="value"?"type":"",s==="valueValue"?"valueType":"",s==="keyValue"?"keyType":""].some(c=>c&&typeof r[c]=="string"&&pf.has(r[c]))&&typeof i=="string"&&t.add(i),n(i,o+1)}},"walk");return n(e,0),t.delete(""),t}function lr(e,t){return JSON.stringify(e)===JSON.stringify(t)}function rd(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]);for(let r of n)if(!(ff.has(r)||r==="nodes"||r==="configs")&&!lr(e[r],t[r]))return!0;return!1}function hf(e,t,n){let r=new Set([n]),o=typeof e.g=="string"?e.g:void 0;for(let s=0;o&&s<32&&!r.has(o);s++){r.add(o);let i=t.get(o);o=i&&typeof i.g=="string"?i.g:void 0}for(let s of Object.values(e))typeof s=="string"&&s!==e.id&&t.get(s)?.z===void 0&&t.has(s)&&r.add(s);for(let[s,i]of t)i.type==="global-config"&&r.add(s);return[...r]}function mf(e,t){return e.filter(n=>n.nodeId===t).map(n=>n.property).join(", ")}function gf(e,t,n,r){let o=[],s=df.filter(d=>!lr(n[d],r[d]));s.length>0&&o.push({code:"secret_destination_changed",error:`SECURITY: node ${e} holds a secret (${t}) restored from the live flow, and this proposal changes where it sends data (${s.join(", ")}). Verify the new destination before approving.`,nodeId:e,property:s.join(",")});let i=[...Ja].filter(d=>!lr(n[d],r[d]));return i.length>0&&o.push({code:"secret_node_code_changed",error:`Node ${e} holds a secret (${t}) and its code changed (${i.join(", ")}). Check the new code does not send the secret anywhere unexpected.`,nodeId:e,property:i.join(",")}),lr(n.wires,r.wires)||o.push({code:"secret_wires_changed",error:`SECURITY: node ${e} holds a secret (${t}) and its output wires changed. Check where its messages (which may carry the secret) now go.`,nodeId:e,property:"wires"}),o}function yf(e,t,n,r,o,s){let i=[];for(let d of hf(t,n,o)){if(d===e||s.has(d))continue;let l=n.get(d),c=d===o,u=r.get(d)??(l&&!c&&l.z===void 0?l:void 0);if(!(c?!lr(l?.env,u?.env):l!==void 0&&u!==void 0&&rd(l,u)))continue;s.add(d);let f=c?"tab":typeof l?.type=="string"?l.type:"node";i.push({code:"secret_scope_changed",error:`SECURITY: ${f} ${d} scopes or configures secret-holding node ${e} (env vars like \${HOST} or a config node) and it changed. Check URLs/hosts it resolves to.`,nodeId:d,property:c?"env":"config"})}return i}function wf(e,t,n,r,o){let s=[],i=[];for(let[d,l]of n){if(d===r)continue;let c=t.get(d);if(c&&!rd(c,l))continue;e.includes(d)||i.push(d);let u=typeof l.type=="string"?l.type:"node";uf(l)&&!e.includes(d)&&s.push({code:"outbound_node_changed",error:`SECURITY: this proposal ${c?"changes":"adds"} outbound-capable node ${d} (${u}). Check where it sends data before approving.`,nodeId:d,property:"type"});let p=c?Ga(c):new Set,f=[...Ga(l)].filter(h=>!p.has(h)&&o.matches(h));f.length>0&&s.push({code:"secret_reference_added",error:`SECURITY: node ${d} (${u}) now reads secret-named value(s) ${f.slice(0,5).join(", ")} (env / global / flow). The AI cannot see these values but the flow will use them at runtime; check they are not sent anywhere unexpected.`,nodeId:d,property:f.slice(0,5).join(",")})}if(e.length===0)return s;for(let[d,l]of t)l.z===r&&!n.has(d)&&i.push(d);if(i.length>0){let d=i.slice(0,10).join(", ")+(i.length>10?", \u2026":"");s.push({code:"secret_tab_changed",error:`This proposal restores secrets in this tab and also changes ${i.length} other node(s) (${d}). Review every change: any of them could route secret data elsewhere.`,nodeId:r,property:"nodes"})}return s}function bf(e,t){let n=new Map;n.set(t,e);for(let r of["nodes","configs"]){let o=e[r];if(Array.isArray(o))for(let s of o)Z(s)&&typeof s.id=="string"&&n.set(s.id,s)}return n}function od(e,t,n,r=dt()){let o=[...new Set(e.map(u=>u.nodeId))],s=String(t.id),i=new Map;for(let u of n)Z(u)&&typeof u.id=="string"&&i.set(u.id,u);let d=bf(t,s),l=[],c=new Set;for(let u of o){let p=i.get(u),f=d.get(u);!p||!f||(l.push(...gf(u,mf(e,u),p,f)),l.push(...yf(u,p,i,d,s,c)))}return l.push(...wf(o,i,d,s,kn(r))),l}var dr,Wp,Gp,Ws,Ka,Kp,Ja,Va,Ya,Ua,Ba,fo,qa,Yp,za,Gs,Zs,Wa,sf,pr,df,lf,cf,pf,ff,Pe=q(()=>{"use strict";un();ke();un();dr="_aiRedactions",Wp=new Set(["TYPE","TYPES","MODE","METHOD","FIELD","FIELDS","PROP","PROPERTY","FORMAT","TIMEOUT","EXPIRY","EXPIRES","TTL","LENGTH","LEN","SIZE","COUNT","ENABLED","DISABLED","ENABLE","USE","URL","URI","ENDPOINT","HOST","PORT","PATH","HEADER","NAME","LABEL","FILE"]),Gp=new Set(["basic","digest","bearer","none","true","false","null","undefined","oauth","oauth2","jwt","apikey","api_key","token","password","header","query","body","cookie","msg","flow","global","env","str","num","json","bool","other"]),Ws=new Set(["env","msg","flow","global","jsonata","bool","date","node","cred"]),Ka=new Set(["id","type","z","g","x","y","w","h","wires","inputs","outputs","links","scope","icon","color","style","d","l","in","out","rev"]),Kp=new Set(["name","label","info"]),Ja=new Set(["func","initialize","finalize"]),Va=256*1024,Ya=new Set(["nodes","configs","subflows"]),Ua=["name","p","keyValue","key","property","k"],Ba={value:"type",v:"vt",to:"tot",valueValue:"valueType"},fo=new Map;a(kn,"createSecretNameMatcher");a(dt,"currentSecretNamePatterns");a(cr,"isSecretLikeValue");qa=new WeakMap;a(Jp,"contentRules");a(Vp,"buildContentRules");Yp=["PASSWORD","PASSWD","PASSPHRASE","SECRET","CLIENT_SECRET","TOKEN","API_KEY","APIKEY","PRIVATE","CREDENTIAL","PWD","PW"];a(Xp,"shannonEntropy");a(Zp,"looksRandom");a(Qp,"unquotedRule");a(ef,"decodeURIComponentSafe");a(tf,"parseConnSegment");a(nf,"connectionStringSpans");za=new WeakMap;a(rf,"scanTextForSecrets");a(Vs,"classifyPlaceholders");a(Z,"isPlainObject");a(of,"isFlowEntity");Gs=class{constructor(t,n){this.ctx=t;this.entityId=n}static{a(this,"EntityRedactor")}paths=[];record(t,n){this.paths.includes(t)||this.paths.push(t),this.ctx.count+=n.length||1,this.ctx.secrets?.set(`${this.entityId}\0${t}`,n)}string(t,n,r){if(r&&cr(t)||t.length>Va)return this.record(n,[t]),this.ctx.fields?.set(`${this.entityId}\0${n}`,{text:J,occurrences:[{kind:"secret",value:t}]}),J;let o=n.split(/[.[]/,1)[0]??"",s=rf(t,this.ctx.matcher,{code:Ja.has(o)});return this.ctx.fields&&s.text.includes(J)&&this.ctx.fields.set(`${this.entityId}\0${n}`,{text:s.text,occurrences:Vs(t,s.spans)}),s.secrets.length>0?(this.record(n,s.secrets),s.text):t}env(t,n){return t.map((r,o)=>{if(!Z(r))return r;let s=typeof r.type=="string"?r.type:"";if(s==="cred"||typeof r.value!="string"||Ws.has(s))return r;let i=typeof r.name=="string"?r.name:"",d=r.value,l=this.string(d,`${n}[${o}].value`,this.ctx.matcher.matches(i));return l===d?r:{...r,value:l,redacted:!0}})}value(t,n,r,o){if(typeof t=="string"){if(Kp.has(r))return this.string(t,n,!1);let s=o?.[`${r}Type`],i=typeof s=="string"&&Ws.has(s);return this.string(t,n,!i&&this.ctx.matcher.matches(r))}return Array.isArray(t)?r==="env"?this.env(t,n):t.map((s,i)=>this.value(s,`${n}[${i}]`,"",void 0)):Z(t)?this.object(t,n):t}object(t,n){let r=!1;for(let s of Ua){let i=t[s];if(typeof i=="string"&&this.ctx.matcher.matches(i)){r=!0;break}}let o={};for(let[s,i]of Object.entries(t)){let d=n?`${n}.${s}`:s;if(r&&s in Ba&&typeof i=="string"){let l=Ba[s],c=l?t[l]:void 0,u=typeof c=="string"&&Ws.has(c);o[s]=this.string(i,d,!u);continue}if(Ua.includes(s)&&typeof i=="string"){o[s]=this.string(i,d,!1);continue}o[s]=this.value(i,d,s,t)}return o}};a(yo,"redactEntity");a(Ys,"redactValueInner");a(bt,"redactFlowData");a(Ks,"normalizeSecretArrays");a(Xa,"secretValuesChanged");a(Xs,"redactConflictLabels");a(lt,"redactProposalForAi");Zs=Symbol.for("ai-access.rawFlowAdapter"),Wa=new WeakMap;a(kt,"aiRedactingAdapter");a(Za,"isRedactingAdapter");a(Oe,"rawFlowAdapter");a(Xe,"adapterForRequest");a(wo,"countRedactionPlaceholders");a(ur,"placeholderContexts");a(go,"contextKey");a(ho,"indexOccurrences");a(Qa,"restoreString");sf=["name","keyValue","p","property","key","k"];a(vt,"stableElementKey");a(fr,"duplicateEnvWarnings");pr=class extends Error{static{a(this,"SecretAlignmentConflictError")}constructor(){super("A duplicate secret line was removed while its live values changed. Resolve this field against the live flow."),this.name="SecretAlignmentConflictError"}};a(Qs,"preserveLiveSecrets");a(ed,"hasNewLiteralPlaceholder");a(mo,"restoreWalk");a(Js,"withoutKey");a(af,"stripMarkers");a(td,"restoreEntity");a(nd,"restoreRedactedPlaceholders");df=["type","url","host","hostname","server","broker","port","path","method","headers","endpoint","uri","protocol","proxy","tls","to","ret"],lf=new Set(["http request","http response","link out","link call","mqtt out","mqtt-broker","tcp out","tcp request","udp out","e-mail","websocket out","websocket-client","websocket-listener","function","exec","file"]),cf=["url","host","hostname","server","broker","endpoint","uri"];a(uf,"isOutboundNode");pf=new Set(["env","global","flow"]);a(Ga,"referencedNames");ff=new Set(["x","y","w","h","l"]);a(lr,"sameJson");a(rd,"nodeChanged");a(hf,"scopeIds");a(mf,"restoredFieldList");a(gf,"secretNodeWarnings");a(yf,"scopeWarnings");a(wf,"tabWarnings");a(bf,"proposedNodeMap");a(od,"secretChangeWarnings")});function mr(e){if(e===null||typeof e!="object")return JSON.stringify(e)??"undefined";if(Array.isArray(e))return`[${e.map(mr).join(",")}]`;let t=e;return`{${Object.keys(t).filter(r=>t[r]!==void 0).sort().map(r=>`${JSON.stringify(r)}:${mr(t[r])}`).join(",")}}`}function Af(e){return(0,ad.createHash)("sha256").update(mr(e)).digest("hex").slice(0,16)}function ye(e){return Array.isArray(e)?e.filter(t=>t&&typeof t=="object"&&typeof t.id=="string"):[]}function An(e){let t=new Map;for(let n of ye(e.nodes))t.set(n.id,{node:n,config:!1});for(let n of ye(e.configs))t.set(n.id,{node:n,config:!0});return t}function xt(e){let t={};for(let n of ni)e[n]!==void 0&&(t[n]=e[n]);return t}function hr(e,t){let n=new Set([...Object.keys(e??{}),...Object.keys(t??{})]),r=[];for(let o of n)vf.has(o)||mr(e?.[o])!==mr(t?.[o])&&r.push(o);return r.sort()}function sd(e){return e.length>0&&e.every(t=>ri.has(t))}function id(e,t){let n={...e};for(let r of ri)t[r]===void 0?delete n[r]:n[r]=t[r];return n}function At(e){if(!e)return{};let t={};return typeof e.type=="string"&&(t.nodeType=e.type),typeof e.name=="string"&&e.name&&(t.nodeName=e.name.slice(0,120)),t}function ld(e,t){let n=e.find(r=>r.id===t&&(r.type==="tab"||r.type==="subflow"));if(n)return{tab:n,nodes:e.filter(r=>r.id!==t&&r.z===t)}}function Wt(e,t,n){let r=ld(e,t);if(!r)return;let o=new Set(ye(n?.configs).map(c=>c.id)),s=new Set(ye(n?.nodes).map(c=>c.id)),i=[],d=[];for(let c of r.nodes)((o.has(c.id)?!0:s.has(c.id)?!1:c.type!=="group"&&c.x===void 0&&c.y===void 0&&c.wires===void 0)?d:i).push(c);let l={id:t,type:r.tab.type,...xt(r.tab),nodes:i};return d.length>0&&(l.configs=d),l}function bo(e,t){if(typeof e=="string")return e.includes(J)&&(typeof t!="string"||ed(e,t));if(Array.isArray(e)){let n=Array.isArray(t)?t:[],r=new Set;return e.some((o,s)=>{let i=vt(o),d=i===void 0?s:n.findIndex((l,c)=>!r.has(c)&&vt(l)===i);return r.add(d),bo(o,n[d])})}if(e&&typeof e=="object"){let n=t&&typeof t=="object"?t:{};return Object.entries(e).some(([r,o])=>bo(o,n[r]))}return!1}function cd(e){let{flowId:t,proposed:n,liveFlows:r,liveRev:o}=e,s=[],i=a(w=>{s.length<kf&&s.push(w)},"push"),d=ld(r,t);if(!d)return i({kind:"tab_deleted",nodeId:t,message:"The flow tab this proposal edits was deleted in the editor.",choices:[]}),{state:"conflict",rev:o,conflicts:s,liveChangedIds:[t]};let l=e.base??(e.baseRev===o?Wt(r,t,n):void 0);if(l===void 0)return i({kind:"base_unavailable",nodeId:t,message:"This proposal was stored before base snapshots existed and the flows changed since, so live edits cannot be told apart from the proposal. Ask the AI to resubmit, or apply the proposal over the live tab.",choices:["proposed"]}),{state:"conflict",rev:o,conflicts:s,liveChangedIds:[]};let c=Wt(r,t,n),u=An(l),p=An(c),f=An(n),h=[],m=xt(l),g=xt(d.tab),y=xt(n),A=hr(m,g),x=hr(m,y),$=y;A.length>0&&(h.push(t),x.length===0?$=g:i({kind:"tab_changed_both",nodeId:t,fields:[...new Set([...A,...x])].sort(),message:"Both the editor and the proposal changed the flow tab settings.",choices:["live","proposed"]})),$={...$};for(let w of ni)g[w]!==void 0&&$[w]===void 0&&($[w]=w==="env"?[]:dd[w]);let L=new Map,D=new Set([...u.keys(),...p.keys(),...f.keys()]),H=new Map;for(let w of r)w.id!==t&&w.z!==t&&H.set(w.id,w);for(let w of D){let F=u.get(w),I=p.get(w),C=f.get(w),ee=F&&I?hr(F.node,I.node):[],ot=F&&C?hr(F.node,C.node):[];(F&&(!I||ee.length>0)||!F&&I)&&h.push(w),C&&F&&H.has(w)?i({kind:"id_collision",nodeId:w,...At(C.node),message:"This node id now exists outside this flow tab.",choices:["live"]}):F&&I&&C?ot.length===0?L.set(w,I):ee.length===0?L.set(w,C):sd(ee)?L.set(w,{...C,node:id(C.node,I.node)}):sd(ot)&&!Xa(F.node,I.node)?L.set(w,{...I,node:id(I.node,C.node)}):i({kind:"node_changed_both",nodeId:w,...At(I.node),fields:[...new Set([...ee,...ot])].filter(Un=>!ri.has(Un)).sort(),message:"Both the editor and the proposal changed this node.",choices:["live","proposed"]}):F&&I&&!C?ee.length===0?L.set(w,null):i({kind:"node_deleted_in_proposal_modified_live",nodeId:w,...At(I.node),fields:ee,message:"The proposal deletes this node, but it was changed in the editor.",choices:["live","proposed"]}):F&&!I&&C?ot.length===0?L.set(w,null):i({kind:"node_deleted_live",nodeId:w,...At(C.node),fields:ot,message:"This node was deleted in the editor, but the proposal changes it.",choices:["live","proposed"]}):F&&!I&&!C?L.set(w,null):!F&&I&&C?i({kind:"id_collision",nodeId:w,...At(C.node),fields:hr(I.node,C.node),message:"The proposal adds a node whose id was also added in the editor.",choices:["live","proposed"]}):!F&&I&&!C?L.set(w,I):!F&&!I&&C&&(H.get(w)??(w===t?d.tab:void 0)?i({kind:"id_collision",nodeId:w,...At(C.node),message:"The proposal adds a node whose id now exists on another flow tab.",choices:["live"]}):L.set(w,C))}if(s.length>0)return{state:"conflict",rev:o,conflicts:s,liveChangedIds:ei(h)};let T=[],k=[],_=new Set,E=a(w=>{if(_.has(w))return;let F=L.get(w);_.add(w),F&&(F.config?k:T).push(ud(F.node))},"emit");for(let w of ye(n.nodes))E(w.id);for(let w of ye(n.configs))E(w.id);for(let w of ye(c.nodes))E(w.id);for(let w of ye(c.configs))E(w.id);let v={id:t,type:d.tab.type,...$,nodes:T};(k.length>0||Array.isArray(n.configs))&&(v.configs=k);let S=new Set([...T,...k].map(w=>w.id)),N=new Set(r.filter(w=>w.id!==t&&w.z!==t).map(w=>w.id));for(let w of[...T,...k]){let F=[];if(Array.isArray(w.wires)){for(let C of w.wires)if(Array.isArray(C))for(let ee of C)typeof ee=="string"&&!S.has(ee)&&F.push(ee)}if(typeof w.g=="string"&&w.g.length>0&&!S.has(w.g)&&F.push(w.g),w.type==="group"&&Array.isArray(w.nodes))for(let C of w.nodes)typeof C=="string"&&!S.has(C)&&F.push(C);if(Array.isArray(w.links))for(let C of w.links)typeof C=="string"&&!S.has(C)&&!N.has(C)&&F.push(C);let I=[...new Set(F)].sort();I.length>0&&i({kind:"dangling_reference",nodeId:w.id,...At(w),fields:I,message:"After merging, this node points to a node that no longer exists (deleted in the editor).",choices:["live"]}),bo(w,p.get(w.id)?.node)&&i({kind:"redacted_placeholder",nodeId:w.id,...At(w),message:"A [REDACTED] placeholder would be deployed over a real value. Ask the AI to resubmit.",choices:[]})}if(d.tab.type==="subflow"){let w=new Set;for(let F of[d.tab.in,d.tab.out])if(Array.isArray(F)){for(let I of F)if(Array.isArray(I?.wires))for(let C of I.wires)typeof C?.id=="string"&&!S.has(C.id)&&w.add(C.id)}w.size>0&&i({kind:"dangling_reference",nodeId:t,fields:[...w].sort(),message:"A subflow port points to a node that would no longer exist.",choices:[]})}return bo($,g)&&i({kind:"redacted_placeholder",nodeId:t,message:"A [REDACTED] placeholder would be deployed into the tab settings. Ask the AI to resubmit.",choices:[]}),s.length>0?{state:"conflict",rev:o,conflicts:s,liveChangedIds:ei(h)}:Sf(v,n,o,[],ei(h))}function Sf(e,t,n,r,o){let s=gr(e);return{state:s===gr(t)?"clean":"merged",rev:n,conflicts:r,merged:e,mergedHash:s,liveChangedIds:o}}function gr(e){return Af(xf(e))}function xf(e){let t=[...ye(e.nodes),...ye(e.configs)].map(ud).sort((n,r)=>n.id<r.id?-1:n.id>r.id?1:0);return{...dd,...xt(e),nodes:t}}function ud(e){if(!("z"in e))return e;let t={...e};return delete t.z,t}function ei(e){return[...new Set(e)]}function pd(e){let{flowId:t,nodeId:n,choice:r,liveFlows:o}=e,s=[],i=Wt(o,t,e.proposed);if(!i)throw new Error("flow tab deleted");if(e.base===void 0){if(r!=="proposed")throw new Error("invalid choice");return{base:St(i),proposed:St(e.proposed),warnings:s}}let d=St(e.base),l=St(e.proposed);if(r==="proposed"&&(s.push(...fr(d,l).filter(h=>h.nodeId===n)),s.push(...fr(i,l).filter(h=>h.nodeId===n&&!s.some(m=>m.nodeId===h.nodeId)))),n===t){let h=xt(i),m=r==="proposed"?Qs({id:t,type:i.type??"tab",...xt(d)},{id:t,type:i.type??"tab",...xt(l)},{id:t,type:i.type??"tab",...h},s):h;for(let g of ni)delete d[g],h[g]!==void 0&&(d[g]=St(h[g])),delete l[g],m[g]!==void 0&&(l[g]=St(m[g]));return{base:d,proposed:l,warnings:s}}let u=An(i).get(n),p=An(d).get(n),f=An(l).get(n);return r==="proposed"&&p&&f&&u&&ti(l,n,{...f,node:Qs(p.node,f.node,u.node,s)}),ti(d,n,u?{...u,node:St(u.node)}:void 0),r==="live"&&ti(l,n,u?{...u,node:St(u.node)}:void 0),{base:d,proposed:l,warnings:s}}function ti(e,t,n){let r=ye(e.nodes),o=ye(e.configs),s=o.some(l=>l.id===t),i=r.some(l=>l.id===t);if(e.nodes=r.filter(l=>l.id!==t),Array.isArray(e.configs)&&(e.configs=o.filter(l=>l.id!==t)),!n)return;s||!i&&n.config?e.configs=[...ye(e.configs),n.node]:e.nodes=[...e.nodes,n.node]}function St(e){return e===void 0?e:JSON.parse(JSON.stringify(e))}var ad,ni,ri,dd,vf,kf,yr=q(()=>{"use strict";ad=require("crypto");un();Pe();ni=["label","disabled","info","env"],ri=new Set(["x","y"]),dd={label:"",disabled:!1,info:"",env:[]},vf=new Set(["z"]),kf=200;a(mr,"stableStringify");a(Af,"hashTab");a(ye,"asNodes");a(An,"mapTab");a(xt,"tabProps");a(hr,"changedFields");a(sd,"positionOnly");a(id,"withPosition");a(At,"nodeLabel");a(ld,"liveTabFromFlows");a(Wt,"nestedLiveTab");a(bo,"containsNewPlaceholder");a(cd,"rebaseFlowTab");a(Sf,"finish");a(gr,"hashFlowTab");a(xf,"normalizeForHash");a(ud,"withoutZ");a(ei,"dedupe");a(pd,"applyConflictChoice");a(ti,"setNode");a(St,"clone")});function gd(e,t){md=e,vo=t??(()=>Date.now())}function yd(){let e=md();return(Number.isFinite(e)&&e>=1?e:fd)*hd}function Tf(e){return`Proposal expired: it stayed open longer than the proposal TTL (${e} day${e===1?"":"s"}). Ask the AI to resubmit if still needed.`}function ko(e,t=vo()){let n=Date.parse(e.createdAt);return Number.isFinite(n)&&t-n>yd()}function ne(e,t={}){let n=vo(),r=Math.round(yd()/hd),o=0;for(let s of e.list())if(U(s.status)&&!(t.flowId&&s.payload.flowId!==t.flowId)&&!e.isApproving(s.id)&&ko(s,n))try{e.markClosed(s.id,"expired",Tf(r)),o+=1}catch{}return o}function oi(e,t){return cd({flowId:e.payload.flowId,base:e.baseFlow,proposed:e.payload.flow,liveFlows:t.flows,liveRev:t.rev,baseRev:e.expectedRev})}function si(e,t=vo()){let n={rev:e.rev,state:e.state,conflicts:e.conflicts.map(r=>({...r,choices:[...r.choices]})),liveChangedCount:e.liveChangedIds.length,checkedAt:new Date(t).toISOString()};return e.mergedHash&&(n.mergedHash=e.mergedHash),n}function Ze(e,t,n,r={}){let o=oi(t,n);return{proposal:e.isApproving(t.id)&&!r.duringApprove?e.get(t.id):e.recordRebase(t.id,si(o)),result:o}}async function wr(e,t,n={}){let r=ne(e,n.flowId?{flowId:n.flowId}:{}),o=n.live;if(!o&&t)try{o=await Oe(t).getFlows()}catch{}if(!o)return{expired:r,conflicts:0,checked:0};let s=$e(e,o,n);return{...s,expired:r+s.expired}}function $e(e,t,n={}){let r={expired:0,conflicts:0,checked:0};for(let o of e.list())if(U(o.status)&&!(n.flowId&&o.payload.flowId!==n.flowId)&&!e.isApproving(o.id))try{if(ko(o)){r.expired+=ne(e,{flowId:o.payload.flowId});continue}let{proposal:s}=Ze(e,o,t);r.checked+=1,s.status==="conflict"&&(r.conflicts+=1)}catch{}return r}function wd(e,t=500){let n,r,o=!1,s=a(async()=>{if(r)return o=!0,r;r=(async()=>{do{o=!1;try{await e()}catch{}}while(o)})();try{await r}finally{r=void 0}},"exec");return{trigger(){n&&clearTimeout(n),n=setTimeout(()=>{n=void 0,s()},t),n.unref?.()},async flush(){n&&(clearTimeout(n),n=void 0),await s()},cancel(){n&&clearTimeout(n),n=void 0}}}async function Qe(e){if(e)try{return await Oe(e).getFlows()}catch{return}}var fd,hd,md,vo,et=q(()=>{"use strict";Je();yr();Pe();fd=7,hd=864e5,md=a(()=>fd,"ttlDaysProvider"),vo=a(()=>Date.now(),"nowProvider");a(gd,"configureProposalTtl");a(yd,"proposalTtlMs");a(Tf,"ttlExpiredReason");a(ko,"isPastTtl");a(ne,"expireByTtl");a(oi,"rebaseProposal");a(si,"rebaseInfoFrom");a(Ze,"syncOneProposal");a(wr,"syncOpenProposals");a($e,"rebaseOpenProposalsWith");a(wd,"createDebouncedSync");a(Qe,"tryRawLive")});var Rd={};Ki(Rd,{applyFlowNodeOps:()=>Gt,applyReplaceTextToValue:()=>Td,buildPatchedFlowTab:()=>br,collectSetProps:()=>ii,decodeBase64UrlJson:()=>vr,findTextOccurrences:()=>xd,interpretPatch:()=>kr});function Sd(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function Rf(e){if(typeof e!="object"||e===null||Array.isArray(e))throw new b(`add op needs node as an object (got ${Sd(e)})`,"invalid_node")}async function br(e,t,n){return Gt(e,t,n.map(r=>({op:"set",nodeId:r.nodeId,set:r.set})))}function xd(e,t){if(!t)return[];let n=[],r=0;for(;r<=e.length;){let o=e.indexOf(t,r);if(o<0)break;n.push(o),r=o+t.length}return n}function Td(e,t,n,r,o){if(Ad.has(t))throw new b(`Cannot change protected node property '${t}' via replace_text`,"protected_property");if(typeof n!="string"||n.length===0)throw new b("find must be a non-empty string","missing_find");if(typeof r!="string")throw new b("replace must be a string","invalid_replace");if(e===void 0)throw new b(`Field '${t}' was not found on the node`,"field_not_found");if(typeof e!="string")throw new b(`Field '${t}' is not a string (got ${e===null?"null":typeof e})`,"field_not_string");let s=xd(e,n);if(s.length===0)throw new b(`Text not found in field '${t}'`,"text_not_found",{details:{matchCount:0}});let i=o;if(i===void 0){if(s.length>1)throw new b(`Found ${s.length} matches in '${t}'; pass occurrence (1-based) to pick one`,"ambiguous_match",{statusCode:409,details:{matchCount:s.length}});i=1}if(!Number.isInteger(i)||i<1||i>s.length)throw new b(`occurrence must be an integer from 1 to ${s.length}`,"invalid_occurrence",{details:{matchCount:s.length}});let d=s[i-1];if(d===void 0)throw new b(`occurrence must be an integer from 1 to ${s.length}`,"invalid_occurrence",{details:{matchCount:s.length}});return e.slice(0,d)+r+e.slice(d+n.length)}function Pf(e){let t=[];for(let n of e)if(n.op==="add"){let r=n.node;if(typeof r!="object"||r===null||Array.isArray(r)){t.push({code:"invalid_node",error:`add op needs node as an object (got ${Sd(r)})`});continue}let o=r;(!o.id||typeof o.id!="string")&&t.push({code:"missing_node_id",error:"add op needs node.id"});try{yt(o.type,"node.type")}catch(s){s instanceof b&&t.push({code:s.code,error:s.message,nodeId:o.id})}"wires"in o&&wn(o.wires,t,typeof o.id=="string"?o.id:void 0)}else n.op==="set"&&typeof n.set=="object"&&"wires"in n.set&&wn(n.set.wires,t,n.nodeId);return t}function _f(e){let t=new Set;for(let n of e)n.op==="add"&&typeof n.node.id=="string"?t.add(n.node.id):n.op!=="add"&&typeof n.nodeId=="string"&&t.add(n.nodeId);return t}async function Gt(e,t,n,r={}){if(!t)throw new b("flow id is required","missing_flow_id");if(!n.length)throw new b("At least one node edit is required","missing_edits");let o=Pf(n);o.push(...Os([n.flatMap(m=>m.op==="add"?[m.node]:[])],"the batch (add ops)")),at(o);let s=await e.getFlow(t);if(r.expectedRev!==void 0&&s.rev!==r.expectedRev){let m=Array.isArray(s.flow.nodes)?[...s.flow.nodes]:[];return{expectedRev:s.rev,flow:{...s.flow,id:s.flow.id,type:s.flow.type??"tab",nodes:m}}}let i=Array.isArray(s.flow.nodes)?[...s.flow.nodes]:[],d=Array.isArray(s.flow.configs)?[...s.flow.configs]:[],l={...s.flow,id:s.flow.id,type:s.flow.type??"tab",nodes:i,configs:d},c=i.map(m=>({...m})),u=[];for(let m of n)try{m.op==="add"&&r.nodeTypes&&await Ca(r.nodeTypes,m.node.type),vd(c,t,m)}catch(g){if(g instanceof b)u.push({code:g.code,error:g.message,...g.statusCode!==400?{statusCode:g.statusCode}:{},...g.details??{}});else throw g}u.push(...Ms(c,t));let p=_f(n),f=await $s(c,r.nodeTypes,{softWarningNodeIds:p});u.push(...f.errors),at(u);for(let m of n)vd(i,t,m);let h=[...f.warnings];if(r.nodeTypes){let m=await Ds(i,d,r.nodeTypes);h.push(...m.filter(g=>!g.nodeId||p.has(g.nodeId)))}return{expectedRev:s.rev,flow:l,...h.length>0?{warnings:h}:{}}}function bd(e,t,n){if(!t||typeof t!="string")throw new b("Each edit needs a node id","missing_node_id");let r=e.findIndex(o=>o.id===t);if(r<0||!e[r])throw new b(`Node '${t}' was not found in flow '${n}'`,"node_not_found",{statusCode:404});return r}function vd(e,t,n){if(n.op==="add"){let i=n.node;if(!i.id||typeof i.id!="string")throw new b("add op needs node.id","missing_node_id");if(yt(i.type,"node.type"),e.some(d=>d.id===i.id))throw new b(`Node '${i.id}' already exists in flow '${t}'`,"node_already_exists");e.push({...i,id:i.id,type:i.type,z:typeof i.z=="string"&&i.z?i.z:t});return}if(n.op==="remove"){let i=bd(e,n.nodeId,t);e.splice(i,1);return}let r=bd(e,n.nodeId,t),o=e[r];if(!o)throw new b(`Node '${n.nodeId}' was not found in flow '${t}'`,"node_not_found",{statusCode:404});if(n.op==="replace_text"){if(!(n.field in o))throw new b(`Field '${n.field}' was not found on the node`,"field_not_found");let i=Td(o[n.field],n.field,n.find,n.replace,n.occurrence);e[r]={...o,id:o.id,type:o.type,[n.field]:i};return}let s={...o,id:o.id,type:o.type};for(let[i,d]of Object.entries(n.set)){if(Ad.has(i))throw new b(`Cannot change protected node property '${i}' via GET propose`,"protected_property");if(i==="wires"){let l=[];wn(d,l,n.nodeId),at(l)}s[i]=d}e[r]=s}function kd(e){if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean"||typeof e=="bigint")return String(e)}function ii(e){let t={},n=e.set;if(n&&typeof n=="object"&&!Array.isArray(n))for(let[r,o]of Object.entries(n)){let s=kd(o);s!==void 0&&(t[r]=s)}for(let[r,o]of Object.entries(e)){if(!r.startsWith("set.")||r.length<=4)continue;let s=r.slice(4);if(typeof o=="string")t[s]=o;else if(Array.isArray(o)&&typeof o[0]=="string")t[s]=o[0];else{let i=kd(o);i!==void 0&&(t[s]=i)}}return t}function vr(e){let t;try{t=Buffer.from(e,"base64url").toString("utf8")}catch{throw new b("patch must be base64url-encoded JSON","invalid_patch")}try{return JSON.parse(t)}catch{throw new b("patch JSON is malformed","invalid_patch_json")}}function Ef(e,t){if(typeof e.flowId=="string"&&e.flowId)return e.flowId;let n=e.flow;if(n&&typeof n=="object"&&!Array.isArray(n)){let r=n.id;if(typeof r=="string"&&r)return r}return t}function ai(e){return typeof e.id=="string"&&e.id?e.id:typeof e.nodeId=="string"&&e.nodeId?e.nodeId:typeof e.node=="string"&&e.node?e.node:""}function Cf(e){if(e==null||e==="")return;let t;if(typeof e=="number")t=e;else if(typeof e=="string")t=Number(e);else if(typeof e=="boolean"||typeof e=="bigint")t=Number(e);else throw new b("occurrence must be a positive integer","invalid_occurrence");if(!Number.isFinite(t))throw new b("occurrence must be a positive integer","invalid_occurrence");return Math.trunc(t)}function If(e){let t=e.node;if(t!==void 0)return Rf(t),{op:"add",node:t};let n={...e};return delete n.op,{op:"add",node:n}}function Lf(e){let t=ai(e);if(!t)throw new b("replace_text op needs node id","missing_node_id");let n=typeof e.field=="string"?e.field:"";if(!n)throw new b("replace_text op needs field","missing_field");if(typeof e.find!="string")throw new b("replace_text op needs find string","missing_find");if(typeof e.replace!="string")throw new b("replace_text op needs replace string","invalid_replace");let r=Cf(e.occurrence),o={op:"replace_text",nodeId:t,field:n,find:e.find,replace:e.replace};return r!==void 0&&(o.occurrence=r),o}function Nf(e){let t=ai(e);if(!t)throw new b("Each patch.nodes entry needs id","missing_node_id");let n=e.set,r={};if(n&&typeof n=="object"&&!Array.isArray(n))Object.assign(r,n);else for(let[o,s]of Object.entries(e))o==="id"||o==="nodeId"||o==="node"||o==="set"||o==="op"||(r[o]=s);return{op:"set",nodeId:t,set:r}}function Ff(e){let t=typeof e.op=="string"?e.op.toLowerCase():"set";if(t==="add")return If(e);if(t==="remove"){let n=ai(e);if(!n)throw new b("remove op needs id","missing_node_id");return{op:"remove",nodeId:n}}if(t==="replace_text")return Lf(e);if(t!=="set")throw new b("nodes[].op must be 'add', 'remove', 'set', or 'replace_text'","invalid_patch");return Nf(e)}function kr(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new b("patch must be a JSON object","invalid_patch");let n=e;if(n.type==="replace_flow"||n.flow&&typeof n.flow=="object"){let r=Ef(n,t);if(!r)throw new b("patch.replace_flow needs flowId","missing_flow_id");return{kind:"replace_flow",flowId:r,flow:n.flow}}if(Array.isArray(n.nodes)){let r=typeof n.flowId=="string"?n.flowId:t;if(!r)throw new b("patch.nodes needs flow id","missing_flow_id");let o=[];for(let i of n.nodes){if(!i||typeof i!="object"||Array.isArray(i))throw new b("Each patch.nodes entry must be an object","invalid_patch");o.push(Ff(i))}let s=o.filter(i=>i.op==="set").map(i=>({nodeId:i.nodeId,set:i.set}));return{kind:"nodes",flowId:r,ops:o,edits:s}}throw new b("patch must include nodes[] edits or type/flow for replace_flow","invalid_patch")}var Ad,Ao=q(()=>{"use strict";ie();Hs();qt();Ad=new Set(["id","type","z"]);a(Sd,"describeNonObjectNode");a(Rf,"assertAddNodeObject");a(br,"buildPatchedFlowTab");a(xd,"findTextOccurrences");a(Td,"applyReplaceTextToValue");a(Pf,"collectOpShapeIssues");a(_f,"touchedNodeIdsFromOps");a(Gt,"applyFlowNodeOps");a(bd,"requireNodeIndex");a(vd,"applyOneNodeOp");a(kd,"stringifyQueryValue");a(ii,"collectSetProps");a(vr,"decodeBase64UrlJson");a(Ef,"resolveReplaceFlowId");a(ai,"resolveNodeId");a(Cf,"parseOccurrence");a(If,"parseAddOp");a(Lf,"parseReplaceTextOp");a(Nf,"parseSetOp");a(Ff,"parseNodeOp");a(kr,"interpretPatch")});function Kt(e,t){return Ut(e.createdBy)===t.id}function di(e,t){return!t||t.length===0||t.includes(e)}function Of(e){return typeof e=="string"&&_d.has(e)}function xn(e,t){let n=$f[e],r=e!=="status"&&Array.isArray(t);return new b(r?`Invalid ${e} filter: pass it at most once (multiple values are not supported). Allowed: ${n}`:`Invalid ${e} filter. Allowed: ${n}`,"invalid_filter",{details:{parameter:e,allowed:n,hint:`Pass ${e} as ${n}, or omit it.`}})}function So(e,t){if(Array.isArray(t))throw xn(e,t);return t}function Mf(e){return typeof e=="number"?Number.isInteger(e)?e:null:typeof e=="string"&&/^-?\d+$/.test(e.trim())?Number(e.trim()):null}function li(e){return e==null||e===""}function Df(e){if(!li(e)){if(e==="true"||e==="1"||e===!0)return!0;if(e==="false"||e==="0"||e===!1)return!1;throw xn("mine",e)}}function ci(e){if(li(e))return;let t=(Array.isArray(e)?e:[e]).flatMap(r=>typeof r=="string"?r.split(","):[r]),n=[];for(let r of t){let o=typeof r=="string"?r.trim():r;if(o!==""){if(!Of(o))throw xn("status",e);n.includes(o)||n.push(o)}}return n.length>0?n:void 0}function Pd(e,t,n,r){if(li(t))return;let o=Mf(t);if(o===null||o<n||o>r)throw xn(e,t);return o}function xo(e){let t={},n=Df(So("mine",e.mine));n!==void 0&&(t.mine=n);let r=ci(e.status);r&&(t.status=r);let o=So("flowId",e.flowId??e.flow_id??e.flow);typeof o=="string"&&o.length>0&&(t.flowId=o);let s=Pd("limit",So("limit",e.limit),1,Sn);s!==void 0&&(t.limit=s);let i=Pd("offset",So("offset",e.offset),0,Number.MAX_SAFE_INTEGER);return i!==void 0&&(t.offset=i),t}function Jt(e){let t=e.offset+e.returned,n=t<e.total;return{total:e.total,offset:e.offset,...e.limit!==null?{limit:e.limit}:{},hasMore:n,...n?{nextOffset:t}:{}}}function Hf(e,t){let n=e.length,r=t.offset!==void 0&&t.offset>0?t.offset:0;if(t.limit===void 0)return{items:r>0?e.slice(r):e,total:n,limit:null,offset:r};let o=Math.min(Math.max(1,t.limit),Sn);return{items:e.slice(r,r+o),total:n,limit:o,offset:r}}function Tn(e,t,n={},r={}){let o=r.mine===!0?t.id:void 0,s={};o&&(s.linkId=o),r.flowId&&(s.flowId=r.flowId);let d=e.list(s).filter(c=>r.mine!==!1||!Kt(c,t)).filter(c=>di(c.status,r.status)).map(c=>({...pe(lt(c),n),mine:Kt(c,t)})),l=Hf(d,r);return{proposals:l.items,total:l.total,limit:l.limit,offset:l.offset}}function ct(e,t,n,r={}){if(!n)throw new b("Missing proposal id","missing_proposal_id");let o=e.get(n);return{...pe(lt(o),r),mine:Kt(o,t)}}function jf(e){let t={kind:"access-link",linkId:e.id};return typeof e.label=="string"&&e.label&&(t.label=e.label),t}function Rn(e,t,n,r,o,s={}){if(!r)throw new b("Missing proposal id","missing_proposal_id");let i;try{i=e.get(r)}catch(f){throw f instanceof me,f}if(!Kt(i,n))throw new jt(r);if(!U(i.status))throw new Y(r,i.status);let d=typeof o=="string"&&o.trim()?o.replace(/\0/g,"").trim().slice(0,2e3):"Withdrawn by access link",l=jf(n),c=e.markWithdrawn(r,l,d),u=pe(lt(c),s),p=u.flowLabel||void 0;return t.append("proposal.withdrawn",l,{proposalId:c.id,flowId:c.payload.flowId,title:u.title,displayName:u.displayName,reason:d,...p?{flowLabel:p}:{}}),{...u,mine:!0}}function Uf(e,t,n,r={}){let o;try{o=e.get(t.id)}catch{return{}}if(r.live){$e(e,r.live,{flowId:o.payload.flowId});try{o=e.get(t.id)}catch{return{}}}return U(o.status)?Ye(Ve(e,o,n)):{}}function he(e,t,n,r={},o={}){let s="mine"in n?n:{...pe(n,r),mine:Kt(n,t)},i=0;if(o.live)try{let c=e.get(s.id).payload.flowId;i=$e(e,o.live,{flowId:c}).expired;let u=e.get(s.id);s={...pe(u,r),mine:Kt(u,t)}}catch{}let d=fe(lt(s),r),l={...d,mine:s.mine,...ue(i)};return o.includeConflicts!==!1&&U(d.status)&&Object.assign(l,Uf(e,d,t.id)),l}async function ut(e){let t=await e.getFlows();return{rev:t.rev,flows:t.flows}}async function Me(e,t){if(!t)throw new b("Missing flow id","missing_flow_id");let n=await e.getFlow(t);return{rev:n.rev,flow:yn(t,n.flow)}}function Bf(e){if(typeof e.expectedRev=="string"&&e.expectedRev.length>0)return e.expectedRev;if(typeof e.expected_rev=="string"&&e.expected_rev.length>0)return e.expected_rev}function Ed(e,t){if("nodes"in e&&e.nodes!==void 0&&!Array.isArray(e.nodes))throw new b("payload.nodes must be an array of node ops","invalid_nodes");if(t!==void 0&&!Array.isArray(t))throw new b("nodes must be an array of node ops","invalid_nodes")}async function qf(e,t,n,r,o){let{applyFlowNodeOps:s,interpretPatch:i}=await Promise.resolve().then(()=>(Ao(),Rd));Ed(n,r);let d=Array.isArray(n.nodes)?n.nodes:Array.isArray(r)?r:null;if(!d)throw new b("payload.flow is required","missing_flow");let l=n.flowId,c=typeof l=="string"?l:"";if(!c)throw new b("payload.flowId is required","missing_flow_id");let u=i({nodes:d,flowId:c},c);if(u.kind!=="nodes")throw new b("payload.nodes must be node ops","invalid_patch");let p=await s(e,u.flowId,u.ops,{expectedRev:t,...o?{nodeTypes:o}:{}});if(p.expectedRev!==t)throw new te(t,p.expectedRev,"submit_proposal");return{flowId:u.flowId,flow:p.flow}}async function zf(e,t,n,r,o){let s=n.flowId,i=n.flow,d=!i||typeof i!="object"||Array.isArray(i);if(d&&Ed(n,r),d&&(Array.isArray(n.nodes)||Array.isArray(r))){let l=await qf(e,t,n,r,o);s=l.flowId,i=l.flow}if(typeof s!="string"||s.length===0)throw new b("payload.flowId is required","missing_flow_id");if(!i||typeof i!="object"||Array.isArray(i))throw new b("payload.flow is required","missing_flow");return{flowId:s,flow:i}}function Wf(e,t){try{return{safeExpectedRev:We(e.slice(0,200),"expectedRev"),safeFlowId:We(t.slice(0,200),"flowId")}}catch{throw new b("expectedRev and payload.flowId must be safe path segments","invalid_path_segment")}}function Gf(e,t){let n=nd(it(e),t);return n.unresolved.length>0&&at(n.unresolved.map(r=>({code:"redacted_placeholder_mismatch",error:`Node ${r.nodeId} field ${r.property}: ${r.reason}. [REDACTED] placeholders are restored only when left exactly where they were shown (same node, field, env/header name, and unchanged line). Do not copy, move, or add placeholders; keep those lines unchanged or supply a real new value.`,nodeId:r.nodeId,property:r.property,statusCode:400}))),n}async function Cd(e,t,n){let r=e.type,o=Bf(e),s=e.payload;if(r!=="replace_flow")throw new b("type must be 'replace_flow'","invalid_proposal_type");if(!o)throw new b("expectedRev is required (top-level field next to type and payload)","missing_expected_rev");if(!s||typeof s!="object"||Array.isArray(s))throw new b("payload must be an object","invalid_payload");let i=s,d=Oe(t),l=await d.getFlows(),c=so(l,d.kind);if(l.rev!==o){let T=Ts(d)?.get(o);if(!T)throw new te(o,l.rev,"submit_proposal");c=so(T,d.kind)}let u=await zf(kt(c),o,i,e.nodes,n),{safeExpectedRev:p,safeFlowId:f}=Wf(o,u.flowId),h=u.flow,m=Buffer.byteLength(JSON.stringify(s),"utf8");if(m>K)throw new le("proposal payload",m,K);at(Na(h,f));let y=await c.getFlows();if(y.rev!==p)throw new te(p,y.rev,"submit_proposal");let A=Gf(h,y.flows),x=it(A.flow);at(La(x,f,y.flows));let $=await c.getFlow(f);if($.rev!==p)throw new te(p,$.rev,"submit_proposal");let L=it($.flow);n&&await Ea(n,x);let{errors:D,warnings:H}=await Fa(x,{flowId:f,baseFlow:L,...n?{nodeTypes:n}:{}});return at(D),H.unshift(...fr(L,x).map(T=>({...T})),...od(A.restoredFields,x,y.flows).map(T=>({...T}))),{safeExpectedRev:p,safeFlowId:f,safeFlow:x,safeBaseFlow:L,restoration:A,flowWarnings:H,head:l}}function Id(e){let t={kind:"access-link",linkId:e.id};return e.label!==void 0&&(t.label=e.label),t}async function Tt(e,t,n,r,o,s={},i){let d=await Cd(r,o,i),{safeExpectedRev:l,safeFlowId:c,safeFlow:u,safeBaseFlow:p,restoration:f,flowWarnings:h}=d,m=Id(n),g=n.oauthClientId&&n.oauthUser?{kind:"oauth",clientId:n.oauthClientId,user:n.oauthUser}:m,y=ar(r.title)??Bs(p,u),A=e.create({type:"replace_flow",expectedRev:l,payload:{flowId:c,flow:u},baseFlow:p,...f.restoredFields.length>0?{restoredSecrets:f.restoredFields}:{},title:y,createdBy:g,...h.length>0?{warnings:h}:{}}),x=Ze(e,A,d.head).proposal,$=pe(lt(x),s),L=$.flowLabel||void 0;return t.append("proposal.created",m,{proposalId:x.id,flowId:c,expectedRev:l,...d.head.rev!==l?{liveRev:d.head.rev}:{},...x.rebase?{rebaseState:x.rebase.state}:{},title:$.title,displayName:$.displayName,...L?{flowLabel:L}:{},...h.length>0?{warningCount:h.length}:{},...f.restored>0?{restoredRedactions:f.restored}:{}}),$}async function To(e,t,n,r,o,s,i={},d){if(!r)throw new b("Missing proposal id","missing_proposal_id");let l=e.get(r);if(!Kt(l,n))throw new jt(r);if(!U(l.status))throw new Y(r,l.status);let c=await Cd({type:"replace_flow",...o,payload:o.payload??{flowId:l.payload.flowId}},s,d);if(c.safeFlowId!==l.payload.flowId)throw new b(`payload.flowId must stay '${l.payload.flowId}' (submit a new proposal for another flow)`,"flow_id_mismatch");let u=ar(o.title),p=e.updateBody(r,{baseFlow:c.safeBaseFlow,flow:c.safeFlow,expectedRev:c.safeExpectedRev,restoredSecrets:c.restoration.restoredFields,warnings:c.flowWarnings,...u?{title:u}:{}}),f=Ze(e,p,c.head).proposal,h=pe(lt(f),i);return t.append("proposal.resolved",Id(n),{proposalId:r,flowId:f.payload.flowId,expectedRev:c.safeExpectedRev,...c.restoration.restored>0?{restoredRedactions:c.restoration.restored}:{},title:h.title,displayName:h.displayName,...f.rebase?{rebaseState:f.rebase.state}:{}}),{...h,mine:!0}}async function Ld(e,t,n,r,o,s,i){if(s!=="live"&&s!=="proposed")throw new b("choice must be 'live' or 'proposed'","invalid_choice");if(typeof o!="string"||o.length===0||o.length>200)throw new b("nodeId is required","missing_node_id");let d=await Oe(i).getFlows(),l=e.get(r);if(!U(l.status))throw new Y(r,l.status);let c=Ze(e,l,d).proposal,u=c.rebase?.conflicts.find(m=>m.nodeId===o);if(!u)throw new b("No current conflict for that node (it may already be resolved). Reload the proposal.","conflict_not_found",{statusCode:409});if(!u.choices.includes(s))throw new b("That choice is not available for this conflict.","invalid_choice");let p;try{p=pd({flowId:c.payload.flowId,base:c.baseFlow,proposed:c.payload.flow,liveFlows:d.flows,nodeId:o,choice:s})}catch(m){throw m instanceof pr?new Ke(r,c.rebase?.conflicts??[]):new b("This conflict cannot be resolved by hand.","invalid_choice")}let f=e.updateBody(r,{baseFlow:it(p.base),flow:it(p.proposed),warnings:[...(c.warnings??[]).filter(m=>m.code!=="secret_may_revert"||m.nodeId!==o),...p.warnings]}),h=Ze(e,f,d).proposal;return t.append("proposal.conflict_resolved",n,{proposalId:r,flowId:h.payload.flowId,nodeId:o,choice:s,kind:u.kind,status:h.status}),h}async function Ar(e,t){let n=Oe(e),r;try{r=(await n.getFlows()).rev}catch{return e}if(r===t)return e;let o=Ts(n)?.get(t);if(!o)return e;let s=so(o,n.kind);return e===n?s:kt(s)}function W(e,t="rest"){return Sr(e,t)}var Sn,_d,$f,b,ie=q(()=>{"use strict";Te();ro();Rs();Je();Zr();se();tt();Hs();qt();zt();Fe();yr();et();Pe();a(Kt,"isMineProposal");Sn=200,_d=new Set(["pending","conflict","conflict","approved","rejected","failed","withdrawn","expired","superseded"]);a(di,"matchesStatusFilter");a(Of,"isProposalStatus");$f={status:[..._d].join(", "),limit:`integer 1\u2013${Sn}`,offset:"integer \u2265 0",mine:"true, false, 1, 0",flowId:"one flow tab id"};a(xn,"invalidFilterError");a(So,"singleQueryValue");a(Mf,"parseIntegerFilter");a(li,"isUnset");a(Df,"parseMineFilter");a(ci,"parseStatusFilter");a(Pd,"parseRangeFilter");a(xo,"parseListProposalsQuery");a(Jt,"listPageFields");a(Hf,"applyListPagination");a(Tn,"listAccessibleProposals");a(ct,"getAccessibleProposal");a(jf,"accessLinkActor");a(Rn,"withdrawMyProposal");a(Uf,"conflictsForProposal");a(he,"presentAccessibleProposalForApi");b=class extends Error{static{a(this,"AiOpsValidationError")}statusCode;code;details;constructor(t,n="validation_error",r={}){super(t),this.name="AiOpsValidationError",this.code=n,this.statusCode=r.statusCode??400,r.details&&(this.details=r.details)}};a(ut,"listFlows");a(Me,"getFlow");a(Bf,"resolveExpectedRev");a(Ed,"assertNodesArrayOrAbsent");a(qf,"materializePayloadFromNodes");a(zf,"resolveReplaceFlowPayload");a(Wf,"assertSafeRevAndFlowId");a(Gf,"strictRestoreOrThrow");a(Cd,"prepareReplaceFlowSubmission");a(Id,"auditActorFor");a(Tt,"submitReplaceFlowProposal");a(To,"resolveMyProposal");a(Ld,"resolveConflictChoice");a(Ar,"adapterAtRev");a(W,"aiOpsErrorToHttp")});function ui(e){return e==="get_link"?86400:2592e3}function pi(e,t){return e==="get_link"?`get_link ttlSeconds must be \u2264 ${t} (24 hours)`:`ttlSeconds must be \u2264 ${t} (30 days) for ${e} tokens`}var Vt,xr=q(()=>{"use strict";a(ui,"maxTtlSecondsForKind");a(pi,"ttlCapErrorMessage");Vt="ttlSeconds must be a positive number of seconds within the cap for the token kind: get_link \u2264 86400 (24h); rest and mcp \u2264 2592000 (30 days)."});function R(e,t,n,r,o=re,s="error"){return{code:e,status:t,meaning:n,hint:r,channels:o,kind:s}}function Tr(e){return Jf.get(e)?.hint}function Nd(e){return e.replace(/\\/g,"\\\\").replace(/\|/g,"\\|")}function Fd(){let e=a(s=>`| \`${s.code}\` | ${s.status} | ${Nd(s.meaning)} | ${Nd(s.hint)} | ${s.channels.join(", ")} |`,"row"),t=["| Code | HTTP | Meaning | Next step | Channels |","| --- | --- | --- | --- | --- |"],n=Po.filter(s=>s.kind==="error"),r=Po.filter(s=>s.kind==="warning"),o=Po.filter(s=>s.kind==="status");return[...t,...n.map(e),"","**Warnings** (in `warnings[]` on a successful propose; never blocking):","",...t,...r.map(e),"","**Proposal statuses.** Open proposals are closed automatically: `expired` after the configured TTL (default 7 days). Revision changes auto-rebase open proposals; `conflict` blocks approval until resolved. Legacy closed proposals stay closed. Submit, get, and list responses include `autoClosedCount` when that request auto-closed proposals (omitted when zero).","",...t,...o.map(e)].join(`
|
|
10
|
-
`)}var re,Pn,Ro,Kf,Rt,Po,Jf,_o=q(()=>{"use strict";xr();re=["GET","REST","MCP"],Pn=["GET"],Ro=["REST","MCP"],Kf=["GET","REST"],Rt=["admin"];a(R,"e");Po=[R("duplicate_env_keys",200,"Changed env arrays contain duplicate names; pairing by name is ambiguous when entries are reordered","Review the env values before approval",[...re,"admin"],"warning"),R("approve_queue_timeout",504,"Approval exceeded its queue-wait deadline (default 60 seconds); no deployment was started for this request","Check the live flow and retry approval",Rt),R("deploy_timeout",504,"Deployment exceeded its deadline (default 60 seconds); approvals wait for settlement or the hard cap (default 5 minutes)","Before the cap, late settlement updates the proposal; after the cap, deployPending.queueReleased is true and the outcome stays unknown. Check the live flow before approving again; approval always re-reads live",Rt),R("secret_may_revert",200,"Secret spans could not be aligned during proposed conflict resolution; proposed values were kept","Review the warned field against live flows before approval; an editor secret change may be reverted",[...re,"admin"],"warning"),R("proposal_conflict",409,"Editor and proposal changes conflict (conflicts contains field names only)","Read the current flow and resolve the proposal before approval",[...re,"admin"]),R("rebase_ack_required",409,"The merged result needs review (mergedHash included)","Review the diff and approve with its mergedHash",["admin"]),R("rebase_race",409,"Flows kept changing during approval","Recheck the proposal and retry approval",["admin"]),R("conflict_not_found",409,"The requested conflict no longer exists","Reload the proposal and its conflicts",["admin"]),R("invalid_choice",400,"Invalid manual conflict choice","Use an available live or proposed choice",["admin"]),R("flow_id_mismatch",400,"Resolution tried to change the proposal flow id","Keep the same flowId or submit a new proposal",Ro),R("access_link_missing",401,"No Bearer / path token","Ask the user for a new access link of the right kind"),R("bearer_scheme_required",401,"Auth used Basic (or non-Bearer)","Send `Authorization: Bearer <token>`",Ro),R("token_expired",401,"Known token past `expiresAt` (body includes `expiredAt` and `accessLinksUrl`)","Ask the user to create a new link on the `/ai-access` dashboard \u2192 Access tokens (`accessLinksUrl`)"),R("access_link_invalid",401,"Unknown, revoked, or cleared after restart","Ask the user for a new access link"),R("wrong_token_kind",403,"Token kind does not match this surface","Mint the matching kind on `/ai-access` \u2192 Access tokens; show `tellUser` to the human"),R("ai_features_disabled",403,"Plugin AI features off","Owner enables AI Access / `adminAuth`"),R("auth_required",401,"Admin session required","Sign in to the Node-RED editor",Rt),R("insufficient_permissions",403,"Admin lacks required scope","Need `*` for link mint / approve",Rt),R("invalid_link_kind",400,"Unknown `kind` on create","Use `get_link`, `rest`, or `mcp`",Rt),R("invalid_ttl",400,"`ttlSeconds` is zero, negative, not a number, or over the cap for the kind",Vt,Rt),R("access_link_limit",409,"Too many active links","Revoke an existing link first",Rt),R("access_link_not_found",404,"Unknown link id on revoke","List links and use an existing id",Rt),R("link_create_not_supported",403,"Attempted mint under a GET link","Admin mints via `POST /mcp/api/v1/links` only",Pn),R("rate_limited",429,"Per-IP or per-token limit","Wait `retryAfter` / `Retry-After`"),R("payload_too_large",413,"Body / proposal over ~1.5MB","Shrink content"),R("invalid_json",400,"Malformed JSON body","Fix Content-Type / body",Ro),R("unsupported_media_type",415,"POST body is not `application/json`","Send `Content-Type: application/json`",Ro),R("url_too_long",414,"GET URL over soft cap (~8KB)","Use chunked drafts or REST/MCP",Pn),R("not_found",404,"Unknown path or resource","Re-read docs / discovery"),R("missing_query",400,"Required search `q` missing","Pass `?q=`",Kf),R("invalid_filter",400,"Invalid list filter (`status`, `limit`, `offset`, `mine`); body names `parameter` and `allowed`","Use an allowed value: status one of the proposal statuses; limit integer 1\u2013200; offset integer \u2265 0; mine true|false"),R("invalid_arguments",400,"MCP tool arguments failed schema validation; body names `parameter`","Fix the named argument to match the tool input schema (see tools/list)",["MCP"]),R("invalid_diff_format",400,"Unknown `format` on proposal diff","Use `format=summary` (default) or `format=full`"),R("confirm_required",400,"GET withdraw without `confirm=1`","Repeat the request with `confirm=1`",Pn),R("missing_expected_rev",400,"Proposal submitted without `expectedRev` / `rev`","Read the flow and pass its `rev`"),R("version_mismatch",409,"Unknown or unavailable `rev` / `expectedRev` snapshot","Re-read flows and resubmit"),R("proposal_not_found",404,"Unknown proposal id","List proposals and use an existing id"),R("proposal_not_pending",409,"Action needs an open proposal","Only pending or conflict proposals can withdraw / decide"),R("proposal_not_owned",403,"Withdraw of another token\u2019s proposal","Only the creating token can withdraw"),R("validation_failed",400,"Several validation errors at once (message `N validation errors`)","Fix every entry in errors[] and resubmit."),R("duplicate_node_id",400,"The same node id appears more than once in a full tab or batch","Give every node a unique id"),R("missing_node_id",400,"Node / op without an id","Include a non-empty string id"),R("missing_node_type",400,"Node without a `type`","Set `type` to an installed palette type name"),R("invalid_node_type",400,"`type` is not a string","Use the palette type name exactly"),R("invalid_nodes",400,"`flow.nodes` / `flow.configs` not an array","Send arrays (use `[]` to clear)"),R("invalid_node",400,"Node entry is not an object","Send `{ id, type, \u2026 }` objects"),R("invalid_wires",400,"Bad wires shape, missing target, or more ports than outputs","Wire only to existing node ids / ports"),R("z_mismatch",400,"Node `z` differs from the flow id","Set `z` to the tab id or omit it"),R("node_already_exists",400,"`add` op for an id already in the flow","Use `set` or a new id"),R("protected_property",400,"Tried to change id / type / z","Omit id, type, and z from edits"),R("node_not_found",404,"Unknown node (single error only; several errors \u2192 `validation_failed`)","Re-read the flow"),R("field_not_found",404,"Unknown field on a node","Re-read the node"),R("text_not_found",400,"`replace_text` find had no match","Re-read field; find is exact"),R("ambiguous_match",409,"`replace_text` matched more than once","Pass `occurrence` (1-based) or unique find"),R("unknown_node_type",400,"Proposal uses a type that is not installed","List node-types / palette search; ask user to install"),R("node_type_not_found",404,"Unknown type on node-types detail","List types or search palette"),R("chunk_conflict",409,"Same draft seq re-sent with different data","Re-send identical data or start a new draft",Pn),R("missing_chunks",400,"Draft submit with gaps in seq","Upload every seq from 0 through max",Pn),R("draft_hash_mismatch",400,"Draft sha256 does not match assembled bytes","Send the hex sha256 of the assembled UTF-8",Pn),R("missing_config_node",201,"Warning: node has no (or an unresolved) config node yet","Configure before deploy if needed; see `candidates`",re,"warning"),R("unwired_node",201,"Warning: node has outputs but no wires","Wire it, or ignore while scaffolding",re,"warning"),R("incomplete_wires",201,"Warning: fewer wire ports than outputs","Add ports if outputs should connect",re,"warning"),R("redacted_placeholder_mismatch",400,"A `[REDACTED]` placeholder was added, duplicated, moved, or its env/header name changed","Keep placeholders exactly where they were shown (same line, same name) or supply a real value",re),R("secret_destination_changed",201,"Warning: a node holding a restored secret changed where it sends data (url/host/method/headers/\u2026)","Reviewer must verify the new destination before approving",re,"warning"),R("secret_node_code_changed",201,"Warning: a node holding a restored secret had its code changed","Reviewer must check the code does not send the secret elsewhere",re,"warning"),R("secret_wires_changed",201,"Warning: a node holding a restored secret had its output wires changed","Reviewer must check where its messages now go",re,"warning"),R("secret_scope_changed",201,"Warning: tab/group env or a config node scoping a secret-holding node changed","Reviewer must check env-resolved hosts/URLs (e.g. `${API_HOST}`)",re,"warning"),R("outbound_node_changed",201,"Warning: proposal adds/changes an outbound-capable node (http request/response, mqtt, tcp, udp, email, websocket, link out/call, function, exec, file, or any node with url/host fields)","Reviewer must check it cannot send secret data elsewhere",re,"warning"),R("secret_tab_changed",201,"Warning: proposal with restored secrets also changes other nodes in the tab","Review every change before approving",re,"warning"),R("secret_reference_added",201,"Warning: an added/changed node now reads a secret-named env/global/flow value (env.get, $env, ${\u2026}, typed input)","Reviewer must check the value is not sent anywhere unexpected",re,"warning"),R("conflict",200,"Status: open proposal has conflicting editor changes; approval blocked","Read the current flow and resolve the proposal",re,"status"),R("expired",200,"Status: open proposal expired after the configured TTL (default 7 days)","Re-read the flow and submit a new proposal",re,"status"),R("superseded",200,"Legacy closed status; new approvals rebase other open proposals","Re-read the flow and submit a new proposal if still needed",re,"status"),R("upstream_error",502,"Node-RED runtime/flows error","Retry; check Node-RED logs"),R("internal_error",500,"Unexpected server error","Retry; check Node-RED logs")],Jf=new Map(Po.map(e=>[e.code,e]));a(Tr,"registryHint");a(Nd,"cell");a(Fd,"renderGetLinkErrorTable")});function Od(e){let t=pe(e),n=po(e)||void 0;return{proposalId:e.id,title:t.title,displayName:t.displayName,flowId:e.payload.flowId,...n?{flowLabel:n}:{}}}function fi(e,t){return e!==void 0&&Number.isFinite(e)&&e>0?e:t}var Yt,Xt,Pt,Zt,Eo,Vf,$d,Co,Io=q(()=>{"use strict";Te();Je();Fe();yr();et();Pe();a(Od,"auditProposalMeta");Yt=class extends Error{static{a(this,"ProposalRebaseAckRequiredError")}code="rebase_ack_required";statusCode=409;mergedHash;rebaseState;constructor(t,n,r){super(`Proposal '${t}' was rebased onto newer editor changes since you viewed it. Review the updated diff, then approve again with the new mergedHash.`),this.name="ProposalRebaseAckRequiredError",this.mergedHash=n,this.rebaseState=r}},Xt=class extends Error{static{a(this,"ProposalRebaseRaceError")}code="rebase_race";statusCode=409;constructor(t){super(`Proposal '${t}' could not be applied because the flows kept changing during approve. It is still pending \u2014 try again.`),this.name="ProposalRebaseRaceError"}},Pt=class extends Error{static{a(this,"ProposalDeployTimeoutError")}code="deploy_timeout";statusCode=504;constructor(){super("Deployment timed out; the deploy outcome is unknown. The proposal remains pending with deployPending set. Approvals wait until the runtime settles or the deploy hard cap is reached. Check the live flow before approving again."),this.name="ProposalDeployTimeoutError"}},Zt=class extends Error{static{a(this,"ProposalApproveQueueTimeoutError")}code="approve_queue_timeout";statusCode=504;constructor(){super("Approval timed out waiting in the queue. No deployment was started for this request. Check the live flow and retry."),this.name="ProposalApproveQueueTimeoutError"}},Eo=class extends Error{static{a(this,"DeployHardCapError")}},Vf=3;a(fi,"deadlineMs");$d=Promise.resolve(),Co=class{constructor(t,n,r,o,s={}){this.proposals=t;this.audit=n;this.adapter=r;this.getDeploymentType=o;this.serviceOptions=s}static{a(this,"ProposalService")}async approve(t,n,r={}){let o=this.proposals.get(t),s=o.deployPending!==void 0;if(!U(o.status))throw new Y(t,o.status);if(!s&&this.proposals.isApproving(t))throw new Re(t);let i=$d,d,l=new Promise(c=>{d=c});return $d=i.then(()=>l),new Promise((c,u)=>{let p=!1,f,h=new Promise((g,y)=>{f=setTimeout(()=>y(new Zt),fi(this.serviceOptions.approveQueueTimeoutMs,6e4)),f.unref?.()});a(async()=>{try{await Promise.race([i,h]),clearTimeout(f);let g=this.proposals.get(t);if(s&&g.status==="approved")return g;let y=this.proposals.beginApprove(t);return p=!0,await this.approveLocked(y,n,r,u)}finally{clearTimeout(f),p&&this.proposals.releaseLock(t),d()}},"run")().then(c,u)})}async approveLocked(t,n,r,o){let s=t.id,i,d=Od(t),l=r.deploymentType??this.getDeploymentType(),c=t.type;if(c!=="replace_flow"){let f=this.proposals.markFailed(s,n,`Unsupported proposal type '${c}'`);return this.audit.append("proposal.failed",n,{...d,reason:f.reason}),f}if(ko(t))throw this.proposals.releaseLock(s),ne(this.proposals,{flowId:t.payload.flowId}),new Y(s,this.proposals.get(s).status);let u=Oe(this.adapter),p=a((f,h)=>{let m=this.proposals.markApproved(s,n,h);return this.audit.append("proposal.approved",n,{...d,resultRev:h,liveRev:h,baseRev:f.expectedRev,alreadyApplied:!0}),m},"markAlreadyApplied");for(let f=0;f<Vf;f++){let h=await u.getFlows(),m=this.proposals.get(s),g=Wt(h.flows,m.payload.flowId,m.payload.flow),y=g&&gr(g);if(y&&(y===m.deployPending?.mergedHash||y===gr(m.payload.flow))){i=p(m,h.rev);break}let{result:A}=Ze(this.proposals,m,h,{duringApprove:!0});if(A.state==="conflict"||!A.merged||!A.mergedHash)throw this.audit.append("proposal.conflict",n,{...d,liveRev:h.rev,conflictCount:A.conflicts.length}),new Ke(s,si(A).conflicts);if(y===A.mergedHash){i=p(m,h.rev);break}if((A.state==="merged"||r.mergedHash!==void 0)&&r.mergedHash!==A.mergedHash)throw new Yt(s,A.mergedHash,A.state);let $=A.merged,L=A.mergedHash,D=!1;try{let H=await this.withDeployDeadline(()=>u.updateFlow({flowId:m.payload.flowId,flow:$,expectedRev:h.rev,deploymentType:l}),()=>{D=!0;let T=new Pt;this.proposals.recordDeployPending(s,{mergedHash:L,reason:"Deploy outcome unknown: the runtime has not settled. Approvals are waiting."}),this.audit.append("proposal.deploy_timeout",n,{...d,code:T.code}),o(T)},()=>{this.proposals.recordDeployPending(s,{mergedHash:L,queueReleased:!0,reason:"Deploy outcome unknown: the runtime did not settle before the hard cap. The approval queue was released. Check the live flow before approving again."}),o(new Pt)});i=this.proposals.markApproved(s,n,H.rev),this.audit.append("proposal.approved",n,{...d,resultRev:H.rev,flowId:m.payload.flowId,baseRev:m.expectedRev,liveRev:h.rev,rebaseState:A.state});break}catch(H){if(i||H instanceof Eo)throw H;if(D)throw this.proposals.recordDeployPending(s,void 0),H;if(H instanceof te)continue;let T=H instanceof Error?H.message:String(H),k=this.proposals.markFailed(s,n,T);return this.audit.append("proposal.failed",n,{...d,reason:T}),k}}if(!i)throw new Xt(s);try{await wr(this.proposals,this.adapter,{flowId:i.payload.flowId})}catch{}return i}async withDeployDeadline(t,n,r){let o=setTimeout(n,fi(this.serviceOptions.deployTimeoutMs,6e4));o.unref?.();let s,i=new Promise((d,l)=>{s=setTimeout(()=>{try{r()}finally{l(new Eo)}},fi(this.serviceOptions.deployHardCapMs,3e5)),s.unref?.()});try{return await Promise.race([t(),i])}finally{clearTimeout(o),clearTimeout(s)}}reject(t,n,r){let o=this.proposals.get(t);if(!U(o.status))throw new Y(t,o.status);let s=Od(o),i=this.proposals.markRejected(t,n,r.trim()||"Rejected by admin");return this.audit.append("proposal.rejected",n,{...s,reason:i.reason}),i}}});function we(e,t,n={}){return hi(e,t,n)}function Yf(e,t){return t==="excess_ports"?e==="mcp"?"This node has more wire ports than its type declares \u2014 remove extra ports or raise the node outputs, then retry.":e==="rest"?"This node has more wire ports than its type declares \u2014 trim wires[] or raise outputs, then retry.":"This node has more wire ports than its type declares \u2014 trim wires or raise outputs, then retry.":t==="missing_target"?e==="mcp"?"Wire targets must be node ids in this flow \u2014 add the node in the same patch or use get_flow ids.":e==="rest"?"Wire targets must be node ids in this flow \u2014 add the node in the same patch or use GET /flows/{id} ids.":"wires must list existing node ids (or add those nodes in the same patch).":e==="mcp"||e==="rest"?"Fix wires: targets must exist in this flow, and port count must not exceed the node outputs.":"wires must list existing node ids (or add those nodes in the same patch)."}function Xf(e){return e==="mcp"?"Call list_node_types or search_palette. Ask the user to install via Manage palette, then recheck. This API never installs.":e==="rest"?"Call GET /node-types?q= or GET /palette/search?q=. Ask the user to install via Manage palette, then recheck. This API never installs.":"Call GET \u2026/node-types?q= or \u2026/palette/search?q=. Ask the user to install via Manage palette, then recheck. This API never installs."}function _t(e,t,n,r){return e==="mcp"?t:e==="rest"?n:r}function hi(e,t,n={}){switch(e){case"version_mismatch":return _t(t,"Re-read with list_flows/get_flow and retry with the current rev.","Re-read the flow and retry with the current rev.","Re-read the flow and retry with the current rev.");case"node_not_found":return _t(t,"Call get_flow or get_node and use a node id from that response.","Call GET /flows/{id} and use a node id from that response.","Call GET \u2026/flows/<id> and use a node id from that response.");case"flow_not_found":return _t(t,"Call list_flows to list valid ids.","Call GET /flows to list valid ids.","Use an id from GET ./flows.");case"proposal_not_found":return _t(t,"Call list_proposals and use an id from that response.","List proposals and use an id from that response.","List proposals and use an id from that response.");case"proposal_not_owned":return"Only the access link that created an open proposal can withdraw it.";case"proposal_not_pending":return"Only open (pending or conflict) proposals can be withdrawn or rejected.";case"confirm_required":return"Add confirm=1 to the withdraw URL (required so link prefetchers cannot withdraw).";case"invalid_wires":return Yf(t,n.cause);case"node_type_not_found":case"unknown_node_type":return Xf(t);case"missing_node_type":return'Each add op needs node.type as a non-empty string (e.g. "inject").';case"invalid_line_range":return _t(t,'Pass lines as "A" or "A-B" (1-based inclusive). Reversed ranges (end < start) are invalid.',"Use lines=A or lines=A-B (1-based inclusive). Reversed ranges (end < start) are invalid.","Use lines=A or lines=A-B (1-based inclusive). Reversed ranges (end < start) are invalid.");case"line_range_out_of_bounds":return"start is past the end of the field \u2014 check totalLines and request a lower lines range.";case"ambiguous_match":return"find matched more than once \u2014 pass occurrence=N (1-based) or a more unique find string.";case"missing_expected_rev":return _t(t,"Pass expectedRev from list_flows/get_flow (top-level, next to type and payload).","Pass expectedRev from GET /flows or GET /flows/{id} (top-level, next to type and payload).","Pass rev from GET \u2026/flows or \u2026/flows/<id>.");case"missing_query":return _t(t,"Pass a non-empty q string.","q query parameter is required.","q query parameter is required.");case"unsupported_media_type":return"Send Content-Type: application/json with a JSON body.";case"text_not_found":return"Re-read the node field; find must match exactly (case-sensitive).";case"field_not_found":return"Re-read the node; field must be an existing string property (template, func, \u2026).";case"field_not_string":return _t(t,"This tool only returns string fields (template, func, \u2026). Use get_node for other property types.","This endpoint only returns string fields (template, func, \u2026). Use GET /nodes/{id} for other property types.","This endpoint only returns string fields (template, func, \u2026). Use GET /nodes/{id} for other property types.");default:return}}function Sr(e,t,n={}){let r=n.canonicalizeMessage!==!1,o=null;if(e instanceof Ke?o={statusCode:409,body:{error:e.message,code:e.code,conflicts:e.conflicts}}:e instanceof Yt?o={statusCode:409,body:{error:e.message,code:e.code,mergedHash:e.mergedHash}}:e instanceof Xt||e instanceof Pt||e instanceof Zt||e instanceof Re?o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}:e instanceof b?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,...e.details??{}}}:e instanceof te?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,expectedRev:e.expectedRev,currentRev:e.currentRev}}:e instanceof X?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,flowId:e.flowId}}:e instanceof me||e instanceof jt||e instanceof Y||e instanceof le?o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}:(e instanceof ze||e instanceof Le)&&(o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}),!o)return null;let s=o.body.code;if(r&&!Array.isArray(o.body.errors)){let i=nt[s];i&&(o.body.error=i)}if(typeof o.body.hint!="string"||!o.body.hint){let i=hi(s,t,{cause:o.body.cause})??Tr(s);i&&(o.body.hint=i)}return o}function Md(e,t,n={}){let o={error:n.error??nt[t]??"Request failed",code:t,...n.details??{}},s=hi(t,e,{cause:o.cause})??Tr(t);return s&&(o.hint=s),{statusCode:n.statusCode??400,body:o}}var nt,tt=q(()=>{"use strict";Te();Je();se();ie();_o();Io();nt={proposal_conflict:"Proposal conflicts with current editor changes",rebase_ack_required:"Review and acknowledge the merged proposal",rebase_race:"Flows kept changing during approval",deploy_timeout:"Deployment timed out; the deploy outcome is unknown. The proposal remains pending with deployPending set. Approvals wait until the runtime settles or the deploy hard cap is reached. Check the live flow before approving again.",approve_queue_timeout:"Approval timed out waiting in the queue. No deployment was started for this request. Check the live flow and retry.",conflict_not_found:"Conflict no longer exists",invalid_choice:"Invalid conflict choice",flow_id_mismatch:"Proposal flow id cannot change",node_not_found:"Node was not found",flow_not_found:"Flow was not found",field_not_found:"Field was not found",field_not_string:"Field is not a string",missing_query:"q query parameter is required",missing_flow_id:"Missing flow id",missing_node_id:"Missing node id",missing_field:"Missing field name",invalid_line_range:"lines must be A or A-B (1-based inclusive; end >= start)",line_range_out_of_bounds:"lines range is out of bounds",proposal_not_found:"Proposal was not found",proposal_not_owned:"Proposal was not created by this access link",proposal_not_pending:"Proposal is not pending",confirm_required:"Withdraw over GET requires confirm=1",unsupported_media_type:"Content-Type must be application/json",upstream_error:"Upstream request failed",internal_error:"Internal server error"};a(we,"channelErrorHint");a(Yf,"invalidWiresHint");a(Xf,"nodeTypeHint");a(_t,"pickChannel");a(hi,"hintFor");a(Sr,"mapErrorForChannel");a(Md,"buildChannelError")});var My={};Ki(My,{ACCESS_LINK_INVALID_MESSAGE:()=>Ot,ACCESS_LINK_MISSING_MESSAGE:()=>xe,AccessLinkStore:()=>an,EDITOR_SIDEBAR_ASSET_VERSION:()=>Cn,MAX_PROPOSAL_JSON_BYTES:()=>K,SettingsStore:()=>fn,acceptedMediaTypes:()=>Wc,areAiFeaturesEnabled:()=>Ce,bindSettingsStore:()=>Wr,createPluginServices:()=>us,default:()=>$y,getStorageDir:()=>ns,isAdminAuthEnabled:()=>oe,isMcpStreamRequest:()=>Gc,packageVersion:()=>En,registerRoutes:()=>Kc,renderEditorSidebarJs:()=>Lo,renderEditorSidebarLoaderHtml:()=>Ud,requireAccessLink:()=>Dt,resolveAiAccessSettings:()=>Ee});module.exports=eu(My);var zc=require("fs");var $r=require("fs"),Gn=require("path");se();function Mr(e){let t=(0,Gn.join)(e,".ai-access");return{root:t,proposals:(0,Gn.join)(t,"proposals"),logs:(0,Gn.join)(t,"logs"),settings:(0,Gn.join)(t,"config.json")}}a(Mr,"resolveAiAccessPaths");function ms(e){(0,$r.mkdirSync)(e,{recursive:!0,mode:hs});try{(0,$r.chmodSync)(e,hs)}catch{}}a(ms,"mkdirSecure");function Zi(e){ms(e.root),ms(e.proposals),ms(e.logs)}a(Zi,"ensureAiAccessDirs");$t();Be();function Vr(e){let t=Ae(e);return`auth-tokens${(t==="/"?"":t).replace(/\//g,"-")}`}a(Vr,"editorAuthTokenKey");Be();tt();function Zf(e){if(!e)return!1;let t=e.split(";")[0]?.trim().toLowerCase()??"";return t==="application/json"||t.endsWith("+json")}a(Zf,"isJsonContentType");function Qf(e){let t=e.headers["content-length"];if(typeof t=="string"&&t.length>0){let n=Number(t);if(Number.isFinite(n)&&n>0)return!0;if(n===0)return!1}return e.headers["transfer-encoding"]!==void 0}a(Qf,"hasRequestBody");function De(e="rest"){return(t,n,r)=>{let o=t.headers["content-type"];if(Zf(o)){r();return}if(!o&&!Qf(t)){r();return}let s=Md(e,"unsupported_media_type",{statusCode:415});n.status(s.statusCode).json(s.body)}}a(De,"requireJsonContentType");var eh="text/plain; charset=utf-8",th="text/html; charset=utf-8",nh="default-src 'none'; base-uri 'self'; form-action 'none'; frame-ancestors 'none'";function rh(e){let t=e.query?.format;return typeof t=="string"?t.toLowerCase():void 0}a(rh,"formatParam");function oh(e){let t=rh(e);if(t==="md"||t==="json")return"native";if(!e.get("accept"))return"html";let n=e.accepts(["text/html","application/json","text/markdown","text/plain"]);return n==="application/json"||n==="text/markdown"?"native":n==="text/plain"?"plain":"html"}a(oh,"chooseGetLinkRepresentation");function Dd(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}a(Dd,"escapeHtml");var sh=/(?<![A-Za-z0-9_.~\-/])\.\/[A-Za-z0-9._~\-/]+(?:\?[A-Za-z0-9._~\-=&%]+)?/g;function ih(e){return e.replace(sh,(t,n)=>{if(e.startsWith("<",n+t.length))return t;let r=t.replace(/[.]+$/,""),o=t.slice(r.length);return r.length<=2||r.includes("..")||r.includes("//")||r.includes("&")?t:`<a href="${r}">${r}</a>${o}`})}a(ih,"linkifyEscaped");function ah(e,t){return`<!doctype html>
|
|
11
|
-
<html><head><meta charset="utf-8">${t?`<base href="${
|
|
12
|
-
`}a(
|
|
9
|
+
`);n.push({before:i>=0?o.slice(i+1):o,after:d>=0?s.slice(0,d):s})}return n}function Po(e){return JSON.stringify([e.before,e.after])}function Ro(e){let t=new Map;return xr(e.text).forEach((n,r)=>{let o=Po(n),s=t.get(o)??[];s.push(r),t.set(o,s)}),t}function md(e,t,n,r){if(e===t)return{text:n,restored:r.filter(f=>f.kind==="secret").length};let o=xr(e),s=xr(t);if(o.length>s.length)return{error:`contains ${o.length} [REDACTED] placeholders but the original had ${s.length}`};let i=Ro({text:t,occurrences:r}),d=new Set([...i].filter(([,f])=>new Set(f.map(h=>r[h]?.kind)).size>1).map(([f])=>f)),l=new Map,c=e.split(X),u=c[0]??"",p=0;for(let f=0;f<o.length;f++){let h=o[f];if(!h)break;let m=Po(h),g=i.get(m)??[];if(d.has(m))return{error:"ambiguous literal and secret [REDACTED] occurrences"};let w=l.get(m)??0,v=g[w]??-1;if(v<0)return{error:"a [REDACTED] placeholder is not on an unchanged original line"};l.set(m,w+1);let k=r[v];k?.kind==="secret"&&p++,u+=(k?.value??X)+(c[f+1]??"")}return{text:u,restored:p}}function Xt(e){if(oe(e))for(let t of Sf){let n=e[t];if(typeof n=="string"&&n.length>0)return`${t}=${n}`}}function Tr(e,t){let n=a(s=>[s,...s.nodes??[],...s.configs??[]],"entities"),r=new Map(n(e).map(s=>[s.id,s])),o=[];for(let s of n(t)){let i=r.get(s.id);if(JSON.stringify(i?.env)===JSON.stringify(s.env))continue;let d=a(l=>{let c=new Set;return Array.isArray(l)?l.some(u=>!oe(u)||typeof u.name!="string"||!u.name?!1:c.has(u.name)?!0:(c.add(u.name),!1)):!1},"hasDuplicates");!d(i?.env)&&!d(s.env)||o.push({code:"duplicate_env_keys",...s.id?{nodeId:s.id}:{},property:"env",error:"Duplicate env names make pairing by name ambiguous when entries are reordered. Review the env values before approval."})}return o}function hi(e,t,n,r=[]){let o=a(c=>{let u=new Map;return ci(c,{matcher:Rr(It()),count:0,fields:u}),u},"fieldsFor"),s=o(e),i=o(t),d=o(n),l=a((c,u,p,f,h=f,m=f)=>{if(typeof u=="string"){let g=s.get(`${e.id}\0${f}`);if(!g?.occurrences.some(N=>N.kind==="secret"))return u;let w=i.get(`${t.id}\0${h}`),v=d.get(`${n.id}\0${m}`),k=xr(g.text),$=Ro(g),C=w?Ro(w):new Map,O=v?Ro(v):new Map,q=w?.occurrences.map(N=>N.value)??[],T=w?xr(w.text):[],S=w?.occurrences.some((N,P)=>{let A=T[P];return N.kind==="secret"&&A&&!$.has(Po(A))}),F=!1;return g.occurrences.forEach((N,P)=>{if(N.kind!=="secret")return;let A=k[P],I=A?Po(A):"",b=$.get(I)??[],M=C.get(I)??[],H=O.get(I)??[];if(b.length>1&&M.length>0&&M.length<b.length&&JSON.stringify(b.map(qt=>g.occurrences[qt]?.value))!==JSON.stringify(H.map(qt=>v?.occurrences[qt]?.value)))throw new Sr;let L=a((qt,hu)=>{let da=qt.get(I)??[],[Ss]=da;return da.length===1&&Ss!==void 0&&hu?.occurrences[Ss]?.kind==="secret"?Ss:void 0},"matches"),ee=L($,g),Se=L(C,w),G=L(O,v);if(!C.has(I)&&!S)return;if(ee===void 0||Se===void 0||G===void 0){F=!0;return}let Ve=v?.occurrences[G];w?.occurrences[Se]?.value===N.value&&Ve&&(q[Se]=Ve.value)}),F&&r.push({code:"secret_may_revert",nodeId:e.id,property:h,error:"Secret spans could not be aligned reliably. The proposed value was kept and may revert an editor secret change. Review this field before approval."}),!w||F?u:w.text.split(X).reduce((N,P,A)=>N+(A?q[A-1]??X:"")+P,"")}if(Array.isArray(u)){let g=Array.isArray(c)?c:[],w=Array.isArray(p)?p:[],v=new Set,k=new Set;return u.map(($,C)=>{let O=Xt($),q=a((F,N)=>{let P=O===void 0?C:F.findIndex((A,I)=>!N.has(I)&&Xt(A)===O);return N.add(P),P},"match"),T=q(g,v),S=q(w,k);return l(g[T],$,w[S],`${f}[${T}]`,`${h}[${C}]`,`${m}[${S}]`)})}if(oe(u)){let g=oe(c)?c:{},w=oe(p)?p:{},v=a((k,$)=>k?`${k}.${$}`:$,"path");return Object.fromEntries(Object.entries(u).map(([k,$])=>[k,l(g[k],$,w[k],v(f,k),v(h,k),v(m,k))]).filter(([,k])=>k!==void 0))}return u},"walk");return l(e,t,n,"")}function gd(e,t){let n=li(t,[]);return"error"in md(e,t,t,n)}function To(e,t,n,r,o,s){if(typeof e=="string"){if(!e.includes(X))return e;let i=s.fields.get(`${r}\0${o}`);if(typeof t=="string"&&typeof n=="string"&&(i||n.includes(X))){let d=md(e,t,n,i?.occurrences??li(n,[]));return"error"in d?(s.unresolved.push({nodeId:r,property:o,reason:d.error}),e):(d.restored>0&&(s.restored+=d.restored,s.restoredFields.push({nodeId:r,property:o})),d.text)}return s.unresolved.push({nodeId:r,property:o,reason:"no redacted secret exists at this node id and field"}),e}if(Array.isArray(e)){let i=Array.isArray(t)?t:[],d=Array.isArray(n)?n:[],l=new Set;return e.map(c=>{let u=oe(c)&&c.redacted===!0?di(c,"redacted"):c,p=Xt(u),f=-1;if(p!==void 0){let h=d.map((m,g)=>Xt(m)===p&&!l.has(g)?g:-1).filter(m=>m>=0);h.length>0&&(f=h[0]??-1)}return f>=0?(l.add(f),To(u,i[f],d[f],r,`${o}[${f}]`,s)):To(u,void 0,void 0,r,`${o}[?]`,s)})}if(oe(e)){let i=oe(t)?t:{},d=oe(n)?n:{},l={};for(let[c,u]of Object.entries(e))l[c]=To(u,i[c],d[c],r,o?`${o}.${c}`:c,s);return l}return e}function di(e,t){let n={...e};return delete n[t],n}function Rf(e){let t=di(e,kr);return Array.isArray(t.env)&&(t.env=t.env.map(n=>!oe(n)||n.redacted!==!0?n:di(n,"redacted"))),t}function yd(e,t,n,r){let o=typeof e.id=="string"?e.id:"",s=o?t.get(o):void 0,i={},d=s?ci({...s,id:o},r):void 0;for(let[l,c]of Object.entries(e))if(l!==kr){if(fd.has(l)&&Array.isArray(c)){i[l]=c.map(u=>oe(u)?yd(u,t,n,r):u);continue}if(cd.has(l)){i[l]=c;continue}i[l]=To(c,d?.[l],s?.[l],o,l,n)}return Rf(i)}function wd(e,t,n=It()){let r=new Map;for(let l of t)oe(l)&&typeof l.id=="string"&&r.set(l.id,l);let o=new Map,s={matcher:Rr(n),count:0,fields:o},i={fields:o,restored:0,restoredFields:[],unresolved:[]};return{flow:yd(e,r,i,s),restored:i.restored,restoredFields:i.restoredFields,unresolved:i.unresolved}}function Cf(e){return typeof e.type=="string"&&Pf.has(e.type)?!0:_f.some(t=>typeof e[t]=="string"&&e[t]!=="")}function ld(e){let t=new Set,n=a((r,o)=>{if(!(o>12)){if(typeof r=="string"){if(r.length>pd)return;let s=/(?:\b(?:env|global|flow)\.get|\$env)\([ \t]{0,8}["'`]([^"'`\r\n]{1,128})["'`]|\$\{([A-Za-z_][\w.-]{0,127})\}/g,i;for(;(i=s.exec(r))!==null;)t.add(i[1]??i[2]??"");return}if(Array.isArray(r)){for(let s of r)n(s,o+1);return}if(oe(r))for(let[s,i]of Object.entries(r))[`${s}Type`,s==="v"?"vt":"",s==="to"?"tot":"",s==="value"?"type":"",s==="valueValue"?"valueType":"",s==="keyValue"?"keyType":""].some(c=>c&&typeof r[c]=="string"&&Ef.has(r[c]))&&typeof i=="string"&&t.add(i),n(i,o+1)}},"walk");return n(e,0),t.delete(""),t}function Ln(e,t){return JSON.stringify(e)===JSON.stringify(t)}function vd(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]);for(let r of n)if(!(bd.has(r)||r==="nodes"||r==="configs")&&!Ln(e[r],t[r]))return!0;return!1}function If(e,t,n){let r=new Set([n]),o=typeof e.g=="string"?e.g:void 0;for(let s=0;o&&s<32&&!r.has(o);s++){r.add(o);let i=t.get(o);o=i&&typeof i.g=="string"?i.g:void 0}for(let s of Object.values(e))typeof s=="string"&&s!==e.id&&t.get(s)?.z===void 0&&t.has(s)&&r.add(s);for(let[s,i]of t)i.type==="global-config"&&r.add(s);return[...r]}function Lf(e,t){return e.filter(n=>n.nodeId===t).map(n=>n.property).join(", ")}function Nf(e,t,n,r){let o=[],s=Tf.filter(d=>!Ln(n[d],r[d]));s.length>0&&o.push({code:"secret_destination_changed",error:`SECURITY: node ${e} holds a secret (${t}) restored from the live flow, and this proposal changes where it sends data (${s.join(", ")}). Verify the new destination before approving.`,nodeId:e,property:s.join(",")});let i=[...ud].filter(d=>!Ln(n[d],r[d]));return i.length>0&&o.push({code:"secret_node_code_changed",error:`Node ${e} holds a secret (${t}) and its code changed (${i.join(", ")}). Check the new code does not send the secret anywhere unexpected.`,nodeId:e,property:i.join(",")}),Ln(n.wires,r.wires)||o.push({code:"secret_wires_changed",error:`SECURITY: node ${e} holds a secret (${t}) and its output wires changed. Check where its messages (which may carry the secret) now go.`,nodeId:e,property:"wires"}),o}function Ff(e,t,n,r,o,s){let i=[];for(let d of If(t,n,o)){if(d===e||s.has(d))continue;let l=n.get(d),c=d===o,u=r.get(d)??(l&&!c&&l.z===void 0?l:void 0);if(!(c?!Ln(l?.env,u?.env):l!==void 0&&u!==void 0&&vd(l,u)))continue;s.add(d);let f=c?"tab":typeof l?.type=="string"?l.type:"node";i.push({code:"secret_scope_changed",error:`SECURITY: ${f} ${d} scopes or configures secret-holding node ${e} (env vars like \${HOST} or a config node) and it changed. Check URLs/hosts it resolves to.`,nodeId:d,property:c?"env":"config"})}return i}function Of(e,t,n,r,o){let s=[],i=[];for(let[d,l]of n){if(d===r)continue;let c=t.get(d);if(c&&!vd(c,l))continue;e.includes(d)||i.push(d);let u=typeof l.type=="string"?l.type:"node";Cf(l)&&!e.includes(d)&&s.push({code:"outbound_node_changed",error:`SECURITY: this proposal ${c?"changes":"adds"} outbound-capable node ${d} (${u}). Check where it sends data before approving.`,nodeId:d,property:[...new Set([...Object.keys(c??{}),...Object.keys(l)])].filter(h=>!bd.has(h)&&h!=="nodes"&&h!=="configs"&&!Ln(c?.[h],l[h])).sort().join(",")});let p=c?ld(c):new Set,f=[...ld(l)].filter(h=>!p.has(h)&&o.matches(h));f.length>0&&s.push({code:"secret_reference_added",error:`SECURITY: node ${d} (${u}) now reads secret-named value(s) ${f.slice(0,5).join(", ")} (env / global / flow). The AI cannot see these values but the flow will use them at runtime; check they are not sent anywhere unexpected.`,nodeId:d,property:f.slice(0,5).join(",")})}if(e.length===0)return s;for(let[d,l]of t)l.z===r&&!n.has(d)&&i.push(d);if(i.length>0){let d=i.slice(0,10).join(", ")+(i.length>10?", \u2026":"");s.push({code:"secret_tab_changed",error:`This proposal restores secrets in this tab and also changes ${i.length} other node(s) (${d}). Review every change: any of them could route secret data elsewhere.`,nodeId:r,property:"nodes"})}return s}function $f(e,t){let n=new Map;n.set(t,e);for(let r of["nodes","configs"]){let o=e[r];if(Array.isArray(o))for(let s of o)oe(s)&&typeof s.id=="string"&&n.set(s.id,s)}return n}function kd(e,t,n,r=It()){let o=[...new Set(e.map(u=>u.nodeId))],s=String(t.id),i=new Map;for(let u of n)oe(u)&&typeof u.id=="string"&&i.set(u.id,u);let d=$f(t,s),l=[],c=new Set;for(let u of o){let p=i.get(u),f=d.get(u);!p||!f||(l.push(...Nf(u,Lf(e,u),p,f)),l.push(...Ff(u,p,i,d,s,c)))}return l.push(...Of(o,i,d,s,Rr(r))),l}var kr,cf,uf,ii,cd,pf,ud,pd,fd,od,sd,So,id,mf,ad,ai,fi,dd,Sf,Sr,Tf,Pf,_f,Ef,bd,Ne=W(()=>{"use strict";y();vn();Te();vn();kr="_aiRedactions",cf=new Set(["TYPE","TYPES","MODE","METHOD","FIELD","FIELDS","PROP","PROPERTY","FORMAT","TIMEOUT","EXPIRY","EXPIRES","TTL","LENGTH","LEN","SIZE","COUNT","ENABLED","DISABLED","ENABLE","USE","URL","URI","ENDPOINT","HOST","PORT","PATH","HEADER","NAME","LABEL","FILE"]),uf=new Set(["basic","digest","bearer","none","true","false","null","undefined","oauth","oauth2","jwt","apikey","api_key","token","password","header","query","body","cookie","msg","flow","global","env","str","num","json","bool","other"]),ii=new Set(["env","msg","flow","global","jsonata","bool","date","node","cred"]),cd=new Set(["id","type","z","g","x","y","w","h","wires","inputs","outputs","links","scope","icon","color","style","d","l","in","out","rev"]),pf=new Set(["name","label","info"]),ud=new Set(["func","initialize","finalize"]),pd=256*1024,fd=new Set(["nodes","configs","subflows"]),od=["name","p","keyValue","key","property","k"],sd={value:"type",v:"vt",to:"tot",valueValue:"valueType"},So=new Map;a(Rr,"createSecretNameMatcher");a(It,"currentSecretNamePatterns");a(Ar,"isSecretLikeValue");id=new WeakMap;a(ff,"contentRules");a(hf,"buildContentRules");mf=["PASSWORD","PASSWD","PASSPHRASE","SECRET","CLIENT_SECRET","TOKEN","API_KEY","APIKEY","PRIVATE","CREDENTIAL","PWD","PW"];a(gf,"shannonEntropy");a(yf,"looksRandom");a(wf,"unquotedRule");a(bf,"decodeURIComponentSafe");a(vf,"parseConnSegment");a(kf,"connectionStringSpans");ad=new WeakMap;a(Af,"scanTextForSecrets");a(li,"classifyPlaceholders");a(oe,"isPlainObject");a(xf,"isFlowEntity");ai=class{constructor(t,n){this.ctx=t;this.entityId=n}static{a(this,"EntityRedactor")}paths=[];record(t,n){this.paths.includes(t)||this.paths.push(t),this.ctx.count+=n.length||1,this.ctx.secrets?.set(`${this.entityId}\0${t}`,n)}string(t,n,r){if(r&&Ar(t)||t.length>pd)return this.record(n,[t]),this.ctx.fields?.set(`${this.entityId}\0${n}`,{text:X,occurrences:[{kind:"secret",value:t}]}),X;let o=n.split(/[.[]/,1)[0]??"",s=Af(t,this.ctx.matcher,{code:ud.has(o)});return this.ctx.fields&&s.text.includes(X)&&this.ctx.fields.set(`${this.entityId}\0${n}`,{text:s.text,occurrences:li(t,s.spans)}),s.secrets.length>0?(this.record(n,s.secrets),s.text):t}env(t,n){return t.map((r,o)=>{if(!oe(r))return r;let s=typeof r.type=="string"?r.type:"";if(s==="cred"||typeof r.value!="string"||ii.has(s))return r;let i=typeof r.name=="string"?r.name:"",d=r.value,l=this.string(d,`${n}[${o}].value`,this.ctx.matcher.matches(i));return l===d?r:{...r,value:l,redacted:!0}})}value(t,n,r,o){if(typeof t=="string"){if(pf.has(r))return this.string(t,n,!1);let s=o?.[`${r}Type`],i=typeof s=="string"&&ii.has(s);return this.string(t,n,!i&&this.ctx.matcher.matches(r))}return Array.isArray(t)?r==="env"?this.env(t,n):t.map((s,i)=>this.value(s,`${n}[${i}]`,"",void 0)):oe(t)?this.object(t,n):t}object(t,n){let r=!1;for(let s of od){let i=t[s];if(typeof i=="string"&&this.ctx.matcher.matches(i)){r=!0;break}}let o={};for(let[s,i]of Object.entries(t)){let d=n?`${n}.${s}`:s;if(r&&s in sd&&typeof i=="string"){let l=sd[s],c=l?t[l]:void 0,u=typeof c=="string"&&ii.has(c);o[s]=this.string(i,d,!u);continue}if(od.includes(s)&&typeof i=="string"){o[s]=this.string(i,d,!1);continue}o[s]=this.value(i,d,s,t)}return o}};a(ci,"redactEntity");a(ui,"redactValueInner");a(yt,"redactFlowData");a(pi,"redactConflictLabels");a(nt,"redactProposalForAi");fi=Symbol.for("ai-access.rawFlowAdapter"),dd=new WeakMap;a(Lt,"aiRedactingAdapter");a(hd,"isRedactingAdapter");a(we,"rawFlowAdapter");a(rt,"adapterForRequest");a(_o,"countRedactionPlaceholders");a(xr,"placeholderContexts");a(Po,"contextKey");a(Ro,"indexOccurrences");a(md,"restoreString");Sf=["name","keyValue","p","property","key","k"];a(Xt,"stableElementKey");a(Tr,"duplicateEnvWarnings");Sr=class extends Error{static{a(this,"SecretAlignmentConflictError")}constructor(){super("A duplicate secret line was removed while its live values changed. Resolve this field against the live flow."),this.name="SecretAlignmentConflictError"}};a(hi,"preserveLiveSecrets");a(gd,"hasNewLiteralPlaceholder");a(To,"restoreWalk");a(di,"withoutKey");a(Rf,"stripMarkers");a(yd,"restoreEntity");a(wd,"restoreRedactedPlaceholders");Tf=["type","url","host","hostname","server","broker","port","path","method","headers","endpoint","uri","protocol","proxy","tls","to","ret"],Pf=new Set(["http request","http response","link out","link call","mqtt out","mqtt-broker","tcp out","tcp request","udp out","e-mail","websocket out","websocket-client","websocket-listener","function","exec","file"]),_f=["url","host","hostname","server","broker","endpoint","uri"];a(Cf,"isOutboundNode");Ef=new Set(["env","global","flow"]);a(ld,"referencedNames");bd=new Set(["x","y","w","h","l"]);a(Ln,"sameJson");a(vd,"nodeChanged");a(If,"scopeIds");a(Lf,"restoredFieldList");a(Nf,"secretNodeWarnings");a(Ff,"scopeWarnings");a(Of,"tabWarnings");a($f,"proposedNodeMap");a(kd,"secretChangeWarnings")});function Qt(e){if(e===null||typeof e!="object")return JSON.stringify(e)??"undefined";if(Array.isArray(e))return`[${e.map(Qt).join(",")}]`;let t=e;return`{${Object.keys(t).filter(r=>t[r]!==void 0).sort().map(r=>`${JSON.stringify(r)}:${Qt(t[r])}`).join(",")}}`}function jf(e){return(0,Ad.createHash)("sha256").update(Qt(e)).digest("hex").slice(0,16)}function ke(e){return Array.isArray(e)?e.filter(t=>t&&typeof t=="object"&&typeof t.id=="string"):[]}function Fn(e){let t=new Map;for(let n of ke(e.nodes))t.set(n.id,{node:n,config:!1});for(let n of ke(e.configs))t.set(n.id,{node:n,config:!0});return t}function ot(e){let t={};for(let n of wi)e[n]!==void 0&&(t[n]=e[n]);return t}function Co(e,t){let n=new Set([...Object.keys(e??{}),...Object.keys(t??{})]),r=[];for(let o of n)Sd.has(o)||Qt(e?.[o])!==Qt(t?.[o])&&r.push(o);return r.sort()}function mi(e){if(e===void 0)return new Map;if(!Array.isArray(e))return;let t=new Map;for(let n of e){if(!Nn(n)||typeof n.name!="string"||!n.name||t.has(n.name))return;t.set(n.name,n)}return t}function Nn(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function Rd(e,t){let n=[];return Pr(e,t,e,void 0,n),n.sort()}function Pr(e,t,n,r,o){let s=[],i=a((f,h)=>Qt(f)===Qt(h),"same"),d=new Set([e.type,t.type,n.type].includes("function")?Mf:[]);(!i(e.outputs,t.outputs)||!i(e.outputs,n.outputs))&&(d.add("outputs"),d.add("wires")),[e.type,t.type,n.type].includes("switch")&&(d.add("rules"),d.add("outputs"),d.add("wires"));let l=a(f=>[...d].some(h=>!i(e[h],f[h])),"changedInGroup"),c=l(t)&&l(n),u=a((f,h,m,g)=>{if(i(f,h))return m;if(g==="env"){let w=mi(f),v=mi(h),k=mi(m);if(w&&v&&k){let $=[];for(let C of new Set([...v.keys(),...k.keys(),...w.keys()])){let O=u(w.get(C),v.get(C),k.get(C),`env[${JSON.stringify(C)}]`);O!==void 0&&$.push(O)}return $}}if((g==="credentials"||g.startsWith("credentials.")||g.startsWith("env["))&&Nn(h)&&(Nn(m)||m===void 0&&f===void 0)&&(f===void 0||Nn(f))){let w=Nn(f)?f:{};return p(w,h,Nn(m)?m:{},g)}return o?.push(g),i(f,m)||Df.has(g)?h:(s.push(g),r==="live"?m:h)},"merge"),p=a((f,h,m,g)=>{let w=[];for(let v of new Set([...Object.keys(f),...Object.keys(h),...Object.keys(m)])){if(!g&&Sd.has(v))continue;let k=g?`${g}.${v}`:v,$;!g&&c&&d.has(v)?((!i(f[v],h[v])||!i(f[v],m[v]))&&s.push(v),i(f[v],h[v])||o?.push(v),$=r==="live"?m[v]:h[v]):$=u(f[v],h[v],m[v],k),$!==void 0&&w.push([v,$])}return Object.fromEntries(w)},"mergeObject");return{value:p(e,t,n,""),conflicts:s.sort()}}function Nt(e){if(!e)return{};let t={};return typeof e.type=="string"&&(t.nodeType=e.type),typeof e.name=="string"&&e.name&&(t.nodeName=e.name.slice(0,120)),t}function Td(e,t){let n=e.find(r=>r.id===t&&(r.type==="tab"||r.type==="subflow"));if(n)return{tab:n,nodes:e.filter(r=>r.id!==t&&r.z===t)}}function st(e,t,n){let r=Td(e,t);if(!r)return;let o=new Set(ke(n?.configs).map(c=>c.id)),s=new Set(ke(n?.nodes).map(c=>c.id)),i=[],d=[];for(let c of r.nodes)((o.has(c.id)?!0:s.has(c.id)?!1:c.type!=="group"&&c.x===void 0&&c.y===void 0&&c.wires===void 0)?d:i).push(c);let l={id:t,type:r.tab.type,...ot(r.tab),nodes:i};return d.length>0&&(l.configs=d),l}function Eo(e,t){if(typeof e=="string")return e.includes(X)&&(typeof t!="string"||gd(e,t));if(Array.isArray(e)){let n=Array.isArray(t)?t:[],r=new Set;return e.some((o,s)=>{let i=Xt(o),d=i===void 0?s:n.findIndex((l,c)=>!r.has(c)&&Xt(l)===i);return r.add(d),Eo(o,n[d])})}if(e&&typeof e=="object"){let n=t&&typeof t=="object"?t:{};return Object.entries(e).some(([r,o])=>Eo(o,n[r]))}return!1}function Pd(e){let{flowId:t,proposed:n,liveFlows:r,liveRev:o}=e,s=[],i=a(b=>{s.length<Hf&&s.push(b)},"push"),d=Td(r,t);if(!d)return i({kind:"tab_deleted",nodeId:t,message:"The flow tab this proposal edits was deleted in the editor.",choices:[]}),{state:"conflict",rev:o,conflicts:s,liveChangedIds:[t]};let l=e.base??(e.baseRev===o?st(r,t,n):void 0);if(l===void 0)return i({kind:"base_unavailable",nodeId:t,message:"This proposal was stored before base snapshots existed and the flows changed since, so live edits cannot be told apart from the proposal. Ask the AI to resubmit, or apply the proposal over the live tab.",choices:["proposed"]}),{state:"conflict",rev:o,conflicts:s,liveChangedIds:[]};let c=st(r,t,n),u=Fn(l),p=Fn(c),f=Fn(n),h=[],m=ot(l),g=ot(d.tab),w=ot(n),v=Co(m,g),k=Pr(m,w,g),$=k.value;v.length>0&&h.push(t),k.conflicts.length>0&&i({kind:"tab_changed_both",nodeId:t,fields:k.conflicts,message:"Both the editor and the proposal changed the same flow tab fields.",choices:["live","proposed"]}),$={...$};for(let b of wi)g[b]!==void 0&&$[b]===void 0&&($[b]=b==="env"?[]:xd[b]);let C=new Map,O=new Set([...u.keys(),...p.keys(),...f.keys()]),q=new Map;for(let b of r)b.id!==t&&b.z!==t&&q.set(b.id,b);for(let b of O){let M=u.get(b),H=p.get(b),L=f.get(b),ee=M&&H?Co(M.node,H.node):[],Se=M&&L?Co(M.node,L.node):[];if((M&&(!H||ee.length>0)||!M&&H)&&h.push(b),L&&M&&q.has(b))i({kind:"id_collision",nodeId:b,...Nt(L.node),message:"This node id now exists outside this flow tab.",choices:["live"]});else if(M&&H&&L){let G=Pr(M.node,L.node,H.node);G.conflicts.length===0?C.set(b,{...L,node:G.value}):i({kind:"node_changed_both",nodeId:b,...Nt(H.node),fields:G.conflicts,message:"Both the editor and the proposal changed the same node fields.",choices:["live","proposed"]})}else M&&H&&!L?ee.length===0?C.set(b,null):i({kind:"node_deleted_in_proposal_modified_live",nodeId:b,...Nt(H.node),fields:ee,message:"The proposal deletes this node, but it was changed in the editor.",choices:["live","proposed"]}):M&&!H&&L?Se.length===0?C.set(b,null):i({kind:"node_deleted_live",nodeId:b,...Nt(L.node),fields:Se,message:"This node was deleted in the editor, but the proposal changes it.",choices:["live","proposed"]}):M&&!H&&!L?C.set(b,null):!M&&H&&L?i({kind:"id_collision",nodeId:b,...Nt(L.node),fields:Co(H.node,L.node),message:"The proposal adds a node whose id was also added in the editor.",choices:["live","proposed"]}):!M&&H&&!L?C.set(b,H):!M&&!H&&L&&(q.get(b)??(b===t?d.tab:void 0)?i({kind:"id_collision",nodeId:b,...Nt(L.node),message:"The proposal adds a node whose id now exists on another flow tab.",choices:["live"]}):C.set(b,L))}if(s.length>0)return{state:"conflict",rev:o,conflicts:s,liveChangedIds:gi(h)};let T=[],S=[],F=new Set,N=a(b=>{if(F.has(b))return;let M=C.get(b);F.add(b),M&&(M.config?S:T).push(_d(M.node))},"emit");for(let b of ke(n.nodes))N(b.id);for(let b of ke(n.configs))N(b.id);for(let b of ke(c.nodes))N(b.id);for(let b of ke(c.configs))N(b.id);let P={id:t,type:d.tab.type,...$,nodes:T};(S.length>0||Array.isArray(n.configs))&&(P.configs=S);let A=new Set([...T,...S].map(b=>b.id)),I=new Set(r.filter(b=>b.id!==t&&b.z!==t).map(b=>b.id));for(let b of[...T,...S]){let M=[];if(Array.isArray(b.wires)){for(let L of b.wires)if(Array.isArray(L))for(let ee of L)typeof ee=="string"&&!A.has(ee)&&M.push(ee)}if(typeof b.g=="string"&&b.g.length>0&&!A.has(b.g)&&M.push(b.g),b.type==="group"&&Array.isArray(b.nodes))for(let L of b.nodes)typeof L=="string"&&!A.has(L)&&M.push(L);if(Array.isArray(b.links))for(let L of b.links)typeof L=="string"&&!A.has(L)&&!I.has(L)&&M.push(L);let H=[...new Set(M)].sort();H.length>0&&i({kind:"dangling_reference",nodeId:b.id,...Nt(b),fields:H,message:"After merging, this node points to a node that no longer exists (deleted in the editor).",choices:["live"]}),Eo(b,p.get(b.id)?.node)&&i({kind:"redacted_placeholder",nodeId:b.id,...Nt(b),message:"A [REDACTED] placeholder would be deployed over a real value. Ask the AI to resubmit.",choices:[]})}if(d.tab.type==="subflow"){let b=new Set;for(let M of[d.tab.in,d.tab.out])if(Array.isArray(M)){for(let H of M)if(Array.isArray(H?.wires))for(let L of H.wires)typeof L?.id=="string"&&!A.has(L.id)&&b.add(L.id)}b.size>0&&i({kind:"dangling_reference",nodeId:t,fields:[...b].sort(),message:"A subflow port points to a node that would no longer exist.",choices:[]})}return Eo($,g)&&i({kind:"redacted_placeholder",nodeId:t,message:"A [REDACTED] placeholder would be deployed into the tab settings. Ask the AI to resubmit.",choices:[]}),s.length>0?{state:"conflict",rev:o,conflicts:s,liveChangedIds:gi(h)}:Uf(P,n,o,[],gi(h))}function Uf(e,t,n,r,o){let s=wt(e);return{state:s===wt(t)?"clean":"merged",rev:n,conflicts:r,merged:e,mergedHash:s,liveChangedIds:o}}function wt(e){return jf(Bf(e))}function Bf(e){let t=[...ke(e.nodes),...ke(e.configs)].map(_d).sort((n,r)=>n.id<r.id?-1:n.id>r.id?1:0);return{...xd,...ot(e),nodes:t}}function _d(e){if(!("z"in e))return e;let t={...e};return delete t.z,t}function gi(e){return[...new Set(e)]}function Cd(e){let{flowId:t,nodeId:n,choice:r,liveFlows:o}=e,s=[],i=st(o,t,e.proposed);if(!i)throw new Error("flow tab deleted");if(e.base===void 0){if(r!=="proposed")throw new Error("invalid choice");return{base:Ft(i),proposed:Ft(e.proposed),warnings:s}}let d=Ft(e.base),l=Ft(e.proposed);if(r==="proposed"&&(s.push(...Tr(d,l).filter(h=>h.nodeId===n)),s.push(...Tr(i,l).filter(h=>h.nodeId===n&&!s.some(m=>m.nodeId===h.nodeId)))),n===t){let h=ot(i),m=r==="proposed"?hi({id:t,type:i.type??"tab",...ot(d)},{id:t,type:i.type??"tab",...ot(l)},{id:t,type:i.type??"tab",...h},s):ot(l),g=Pr(ot(d),m,h,r).value;for(let w of wi)delete d[w],h[w]!==void 0&&(d[w]=Ft(h[w])),delete l[w],g[w]!==void 0&&(l[w]=Ft(g[w]));return{base:d,proposed:l,warnings:s}}let u=Fn(i).get(n),p=Fn(d).get(n),f=Fn(l).get(n);if(p&&f&&u){let h=r==="proposed"?hi(p.node,f.node,u.node,s):f.node;yi(l,n,{...f,node:Pr(p.node,h,u.node,r).value})}else r==="live"&&yi(l,n,u?{...u,node:Ft(u.node)}:void 0);return yi(d,n,u?{...u,node:Ft(u.node)}:void 0),{base:d,proposed:l,warnings:s}}function yi(e,t,n){let r=ke(e.nodes),o=ke(e.configs),s=o.some(l=>l.id===t),i=r.some(l=>l.id===t);if(e.nodes=r.filter(l=>l.id!==t),Array.isArray(e.configs)&&(e.configs=o.filter(l=>l.id!==t)),!n)return;s||!i&&n.config?e.configs=[...ke(e.configs),n.node]:e.nodes=[...e.nodes,n.node]}function Ft(e){return e===void 0?e:JSON.parse(JSON.stringify(e))}var Ad,wi,Mf,Df,xd,Sd,Hf,On=W(()=>{"use strict";y();Ad=require("crypto");vn();Ne();wi=["label","disabled","info","env"],Mf=["func","libs","initialize","finalize","outputs"],Df=new Set(["x","y"]),xd={label:"",disabled:!1,info:"",env:[]},Sd=new Set(["z"]),Hf=200;a(Qt,"stableStringify");a(jf,"hashTab");a(ke,"asNodes");a(Fn,"mapTab");a(ot,"tabProps");a(Co,"changedFields");a(mi,"envMap");a(Nn,"isObject");a(Rd,"changedFieldPaths");a(Pr,"mergeFields");a(Nt,"nodeLabel");a(Td,"liveTabFromFlows");a(st,"nestedLiveTab");a(Eo,"containsNewPlaceholder");a(Pd,"rebaseFlowTab");a(Uf,"finish");a(wt,"hashFlowTab");a(Bf,"normalizeForHash");a(_d,"withoutZ");a(gi,"dedupe");a(Cd,"applyConflictChoice");a(yi,"setNode");a(Ft,"clone")});function Ed(e){let t=new Map;for(let n of e.nodes??[])t.set(n.id,n);for(let n of e.configs??[])t.set(n.id,n);return t}function Id(e,t){return Rd(e,t).filter(n=>!qf.has(n))}function Ld(e){let t=Ed(e.baseFlow??{}),n=Ed(e.payload.flow),r=new Map;for(let[d,l]of n){let c=t.get(d);if(!c){r.set(d,new Set(["*"]));continue}let u=Id(c,l);u.length>0&&r.set(d,new Set(u))}for(let[d]of t)n.has(d)||r.set(d,new Set(["*"]));let o=Object.fromEntries(["label","disabled","info","env"].map(d=>[d,e.baseFlow?.[d]])),s=Object.fromEntries(["label","disabled","info","env"].map(d=>[d,e.payload.flow[d]])),i=Id(o,s);return i.length>0&&r.set(e.payload.flowId,new Set(i)),r}function Wf(e,t){let n=[],r=[];for(let[o,s]of e){let i=t.get(o);if(!i)continue;let d=[];for(let c of s){if(c==="*"){d.push(...[...i].filter(u=>u!=="*")),i.has("*")&&d.push("*");continue}(i.has("*")||[...i].some(u=>u===c||u.startsWith(`${c}.`)||c.startsWith(`${u}.`)||u.startsWith(`${c}[`)||c.startsWith(`${u}[`)))&&d.push(c)}let l=[...new Set(d)].sort();l.length>0&&(n.push(o),r.push({nodeId:o,fields:l}))}return n.sort(),{nodeIds:n,fields:r}}function it(e,t,n){let r=Ld(t),o=[];for(let s of e.list()){if(s.id===t.id||!U(s.status)||s.payload.flowId!==t.payload.flowId)continue;let i=si(s),d=n?mt(s.createdBy)===n:mt(s.createdBy)===mt(t.createdBy),l=Wf(r,Ld(s));if(l.nodeIds.length===0)continue;let c={id:s.id,title:i,displayName:oi(i,s.createdAt),mine:d};l.nodeIds.length>0&&(c.overlappingNodeIds=l.nodeIds),l.fields.length>0&&(c.overlappingFields=l.fields),o.push(c)}return{conflictingProposals:o}}function at(e){let t={};return e.conflictingProposals.length>0&&(t.conflictingProposals=e.conflictingProposals,t.warning="Other pending proposals touch the same flow \u2014 approve order may conflict (like a git merge)."),t}function me(e){return e>0?{autoClosedCount:e}:{}}var qf,en=W(()=>{"use strict";y();tt();qe();On();qf=new Set(["x","y","id","z"]);a(Ed,"collectNodes");a(Id,"changedFieldKeys");a(Ld,"computeProposalTouches");a(Wf,"overlapBetween");a(it,"findConflictingProposals");a(at,"conflictWarningFields");a(me,"autoClosedFields")});function $d(e,t){Od=e,Io=t??(()=>Date.now())}function Md(){let e=Od();return(Number.isFinite(e)&&e>=1?e:Nd)*Fd}function zf(e){return`Proposal expired: it stayed open longer than the proposal TTL (${e} day${e===1?"":"s"}). Ask the AI to resubmit if still needed.`}function Lo(e,t=Io()){let n=Date.parse(e.createdAt);return Number.isFinite(n)&&t-n>Md()}function se(e,t={}){let n=Io(),r=Math.round(Md()/Fd),o=0;for(let s of e.list())if(U(s.status)&&!(t.flowId&&s.payload.flowId!==t.flowId)&&!e.isApproving(s.id)&&Lo(s,n))try{e.markClosed(s.id,"expired",zf(r)),o+=1}catch{}return o}function bi(e,t){let n=Pd({flowId:e.payload.flowId,base:e.baseFlow,proposed:e.payload.flow,liveFlows:t.flows,liveRev:t.rev,baseRev:e.expectedRev});return n.state==="clean"&&e.aiConflictOverrides?.length&&(n.state="merged"),n}function vi(e,t=Io()){let n={rev:e.rev,state:e.state,conflicts:e.conflicts.map(r=>({...r,choices:[...r.choices]})),liveChangedCount:e.liveChangedIds.length,checkedAt:new Date(t).toISOString()};return e.mergedHash&&(n.mergedHash=e.mergedHash),n}function We(e,t,n,r={}){let o=bi(t,n);return{proposal:e.isApproving(t.id)&&!r.duringApprove?e.get(t.id):e.recordRebase(t.id,vi(o)),result:o}}async function _r(e,t,n={}){let r=se(e,n.flowId?{flowId:n.flowId}:{}),o=n.live;if(!o&&t)try{o=await we(t).getFlows()}catch{}if(!o)return{expired:r,conflicts:0,checked:0};let s=ze(e,o,n);return{...s,expired:r+s.expired}}function ze(e,t,n={}){let r={expired:0,conflicts:0,checked:0};for(let o of e.list())if(U(o.status)&&!(n.flowId&&o.payload.flowId!==n.flowId)&&!e.isApproving(o.id))try{if(Lo(o)){r.expired+=se(e,{flowId:o.payload.flowId});continue}let{proposal:s}=We(e,o,t);r.checked+=1,s.status==="conflict"&&(r.conflicts+=1)}catch{}return r}function Dd(e,t=500){let n,r,o=!1,s=a(async()=>{if(r)return o=!0,r;r=(async()=>{do{o=!1;try{await e()}catch{}}while(o)})();try{await r}finally{r=void 0}},"exec");return{trigger(){n&&clearTimeout(n),n=setTimeout(()=>{n=void 0,s()},t),n.unref?.()},async flush(){n&&(clearTimeout(n),n=void 0),await s()},cancel(){n&&clearTimeout(n),n=void 0}}}async function dt(e){if(e)try{return await we(e).getFlows()}catch{return}}var Nd,Fd,Od,Io,lt=W(()=>{"use strict";y();tt();On();Ne();Nd=7,Fd=864e5,Od=a(()=>Nd,"ttlDaysProvider"),Io=a(()=>Date.now(),"nowProvider");a($d,"configureProposalTtl");a(Md,"proposalTtlMs");a(zf,"ttlExpiredReason");a(Lo,"isPastTtl");a(se,"expireByTtl");a(bi,"rebaseProposal");a(vi,"rebaseInfoFrom");a(We,"syncOneProposal");a(_r,"syncOpenProposals");a(ze,"rebaseOpenProposalsWith");a(Dd,"createDebouncedSync");a(dt,"tryRawLive")});var Gd={};la(Gd,{applyFlowNodeOps:()=>tn,applyReplaceTextToValue:()=>zd,buildPatchedFlowTab:()=>Cr,collectSetProps:()=>ki,decodeBase64UrlJson:()=>Er,findTextOccurrences:()=>Wd,interpretPatch:()=>Ir});function qd(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function Gf(e){if(typeof e!="object"||e===null||Array.isArray(e))throw new x(`add op needs node as an object (got ${qd(e)})`,"invalid_node")}async function Cr(e,t,n){return tn(e,t,n.map(r=>({op:"set",nodeId:r.nodeId,set:r.set})))}function Wd(e,t){if(!t)return[];let n=[],r=0;for(;r<=e.length;){let o=e.indexOf(t,r);if(o<0)break;n.push(o),r=o+t.length}return n}function zd(e,t,n,r,o){if(Bd.has(t))throw new x(`Cannot change protected node property '${t}' via replace_text`,"protected_property");if(typeof n!="string"||n.length===0)throw new x("find must be a non-empty string","missing_find");if(typeof r!="string")throw new x("replace must be a string","invalid_replace");if(e===void 0)throw new x(`Field '${t}' was not found on the node`,"field_not_found");if(typeof e!="string")throw new x(`Field '${t}' is not a string (got ${e===null?"null":typeof e})`,"field_not_string");let s=Wd(e,n);if(s.length===0)throw new x(`Text not found in field '${t}'`,"text_not_found",{details:{matchCount:0}});let i=o;if(i===void 0){if(s.length>1)throw new x(`Found ${s.length} matches in '${t}'; pass occurrence (1-based) to pick one`,"ambiguous_match",{statusCode:409,details:{matchCount:s.length}});i=1}if(!Number.isInteger(i)||i<1||i>s.length)throw new x(`occurrence must be an integer from 1 to ${s.length}`,"invalid_occurrence",{details:{matchCount:s.length}});let d=s[i-1];if(d===void 0)throw new x(`occurrence must be an integer from 1 to ${s.length}`,"invalid_occurrence",{details:{matchCount:s.length}});return e.slice(0,d)+r+e.slice(d+n.length)}function Kf(e){let t=[];for(let n of e)if(n.op==="add"){let r=n.node;if(typeof r!="object"||r===null||Array.isArray(r)){t.push({code:"invalid_node",error:`add op needs node as an object (got ${qd(r)})`});continue}let o=r;(!o.id||typeof o.id!="string")&&t.push({code:"missing_node_id",error:"add op needs node.id"});try{Ct(o.type,"node.type")}catch(s){s instanceof x&&t.push({code:s.code,error:s.message,nodeId:o.id})}"wires"in o&&Cn(o.wires,t,typeof o.id=="string"?o.id:void 0)}else n.op==="set"&&typeof n.set=="object"&&"wires"in n.set&&Cn(n.set.wires,t,n.nodeId);return t}function Jf(e){let t=new Set;for(let n of e)n.op==="add"&&typeof n.node.id=="string"?t.add(n.node.id):n.op!=="add"&&typeof n.nodeId=="string"&&t.add(n.nodeId);return t}async function tn(e,t,n,r={}){if(!t)throw new x("flow id is required","missing_flow_id");if(!n.length)throw new x("At least one node edit is required","missing_edits");let o=Kf(n);o.push(...Ys([n.flatMap(m=>m.op==="add"?[m.node]:[])],"the batch (add ops)")),gt(o);let s=await e.getFlow(t);if(r.expectedRev!==void 0&&s.rev!==r.expectedRev){let m=Array.isArray(s.flow.nodes)?[...s.flow.nodes]:[];return{expectedRev:s.rev,flow:{...s.flow,id:s.flow.id,type:s.flow.type??"tab",nodes:m}}}let i=Array.isArray(s.flow.nodes)?[...s.flow.nodes]:[],d=Array.isArray(s.flow.configs)?[...s.flow.configs]:[],l={...s.flow,id:s.flow.id,type:s.flow.type??"tab",nodes:i,configs:d},c=i.map(m=>({...m})),u=[];for(let m of n)try{m.op==="add"&&r.nodeTypes&&await Va(r.nodeTypes,m.node.type),jd(c,t,m)}catch(g){if(g instanceof x)u.push({code:g.code,error:g.message,...g.statusCode!==400?{statusCode:g.statusCode}:{},...g.details??{}});else throw g}u.push(...Xs(c,t));let p=Jf(n),f=await Zs(c,r.nodeTypes,{softWarningNodeIds:p});u.push(...f.errors),gt(u);for(let m of n)jd(i,t,m);let h=[...f.warnings];if(r.nodeTypes){let m=await Qs(i,d,r.nodeTypes);h.push(...m.filter(g=>!g.nodeId||p.has(g.nodeId)))}return{expectedRev:s.rev,flow:l,...h.length>0?{warnings:h}:{}}}function Hd(e,t,n){if(!t||typeof t!="string")throw new x("Each edit needs a node id","missing_node_id");let r=e.findIndex(o=>o.id===t);if(r<0||!e[r])throw new x(`Node '${t}' was not found in flow '${n}'`,"node_not_found",{statusCode:404});return r}function jd(e,t,n){if(n.op==="add"){let i=n.node;if(!i.id||typeof i.id!="string")throw new x("add op needs node.id","missing_node_id");if(Ct(i.type,"node.type"),e.some(d=>d.id===i.id))throw new x(`Node '${i.id}' already exists in flow '${t}'`,"node_already_exists");e.push({...i,id:i.id,type:i.type,z:typeof i.z=="string"&&i.z?i.z:t});return}if(n.op==="remove"){let i=Hd(e,n.nodeId,t);e.splice(i,1);return}let r=Hd(e,n.nodeId,t),o=e[r];if(!o)throw new x(`Node '${n.nodeId}' was not found in flow '${t}'`,"node_not_found",{statusCode:404});if(n.op==="replace_text"){if(!(n.field in o))throw new x(`Field '${n.field}' was not found on the node`,"field_not_found");let i=zd(o[n.field],n.field,n.find,n.replace,n.occurrence);e[r]={...o,id:o.id,type:o.type,[n.field]:i};return}let s={...o,id:o.id,type:o.type};for(let[i,d]of Object.entries(n.set)){if(Bd.has(i))throw new x(`Cannot change protected node property '${i}' via GET propose`,"protected_property");if(i==="wires"){let l=[];Cn(d,l,n.nodeId),gt(l)}s[i]=d}e[r]=s}function Ud(e){if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean"||typeof e=="bigint")return String(e)}function ki(e){let t={},n=e.set;if(n&&typeof n=="object"&&!Array.isArray(n))for(let[r,o]of Object.entries(n)){let s=Ud(o);s!==void 0&&(t[r]=s)}for(let[r,o]of Object.entries(e)){if(!r.startsWith("set.")||r.length<=4)continue;let s=r.slice(4);if(typeof o=="string")t[s]=o;else if(Array.isArray(o)&&typeof o[0]=="string")t[s]=o[0];else{let i=Ud(o);i!==void 0&&(t[s]=i)}}return t}function Er(e){let t;try{t=Buffer.from(e,"base64url").toString("utf8")}catch{throw new x("patch must be base64url-encoded JSON","invalid_patch")}try{return JSON.parse(t)}catch{throw new x("patch JSON is malformed","invalid_patch_json")}}function Vf(e,t){if(typeof e.flowId=="string"&&e.flowId)return e.flowId;let n=e.flow;if(n&&typeof n=="object"&&!Array.isArray(n)){let r=n.id;if(typeof r=="string"&&r)return r}return t}function Ai(e){return typeof e.id=="string"&&e.id?e.id:typeof e.nodeId=="string"&&e.nodeId?e.nodeId:typeof e.node=="string"&&e.node?e.node:""}function Yf(e){if(e==null||e==="")return;let t;if(typeof e=="number")t=e;else if(typeof e=="string")t=Number(e);else if(typeof e=="boolean"||typeof e=="bigint")t=Number(e);else throw new x("occurrence must be a positive integer","invalid_occurrence");if(!Number.isFinite(t))throw new x("occurrence must be a positive integer","invalid_occurrence");return Math.trunc(t)}function Zf(e){let t=e.node;if(t!==void 0)return Gf(t),{op:"add",node:t};let n={...e};return delete n.op,{op:"add",node:n}}function Xf(e){let t=Ai(e);if(!t)throw new x("replace_text op needs node id","missing_node_id");let n=typeof e.field=="string"?e.field:"";if(!n)throw new x("replace_text op needs field","missing_field");if(typeof e.find!="string")throw new x("replace_text op needs find string","missing_find");if(typeof e.replace!="string")throw new x("replace_text op needs replace string","invalid_replace");let r=Yf(e.occurrence),o={op:"replace_text",nodeId:t,field:n,find:e.find,replace:e.replace};return r!==void 0&&(o.occurrence=r),o}function Qf(e){let t=Ai(e);if(!t)throw new x("Each patch.nodes entry needs id","missing_node_id");let n=e.set,r={};if(n&&typeof n=="object"&&!Array.isArray(n))Object.assign(r,n);else for(let[o,s]of Object.entries(e))o==="id"||o==="nodeId"||o==="node"||o==="set"||o==="op"||(r[o]=s);return{op:"set",nodeId:t,set:r}}function eh(e){let t=typeof e.op=="string"?e.op.toLowerCase():"set";if(t==="add")return Zf(e);if(t==="remove"){let n=Ai(e);if(!n)throw new x("remove op needs id","missing_node_id");return{op:"remove",nodeId:n}}if(t==="replace_text")return Xf(e);if(t!=="set")throw new x("nodes[].op must be 'add', 'remove', 'set', or 'replace_text'","invalid_patch");return Qf(e)}function Ir(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new x("patch must be a JSON object","invalid_patch");let n=e;if(n.type==="replace_flow"||n.flow&&typeof n.flow=="object"){let r=Vf(n,t);if(!r)throw new x("patch.replace_flow needs flowId","missing_flow_id");return{kind:"replace_flow",flowId:r,flow:n.flow}}if(Array.isArray(n.nodes)){let r=typeof n.flowId=="string"?n.flowId:t;if(!r)throw new x("patch.nodes needs flow id","missing_flow_id");let o=[];for(let i of n.nodes){if(!i||typeof i!="object"||Array.isArray(i))throw new x("Each patch.nodes entry must be an object","invalid_patch");o.push(eh(i))}let s=o.filter(i=>i.op==="set").map(i=>({nodeId:i.nodeId,set:i.set}));return{kind:"nodes",flowId:r,ops:o,edits:s}}throw new x("patch must include nodes[] edits or type/flow for replace_flow","invalid_patch")}var Bd,No=W(()=>{"use strict";y();le();ei();Zt();Bd=new Set(["id","type","z"]);a(qd,"describeNonObjectNode");a(Gf,"assertAddNodeObject");a(Cr,"buildPatchedFlowTab");a(Wd,"findTextOccurrences");a(zd,"applyReplaceTextToValue");a(Kf,"collectOpShapeIssues");a(Jf,"touchedNodeIdsFromOps");a(tn,"applyFlowNodeOps");a(Hd,"requireNodeIndex");a(jd,"applyOneNodeOp");a(Ud,"stringifyQueryValue");a(ki,"collectSetProps");a(Er,"decodeBase64UrlJson");a(Vf,"resolveReplaceFlowId");a(Ai,"resolveNodeId");a(Yf,"parseOccurrence");a(Zf,"parseAddOp");a(Xf,"parseReplaceTextOp");a(Qf,"parseSetOp");a(eh,"parseNodeOp");a(Ir,"interpretPatch")});function nn(e,t){return mt(e.createdBy)===t.id}function Si(e,t){return!t||t.length===0||t.includes(e)}function th(e){return typeof e=="string"&&Jd.has(e)}function Mn(e,t){let n=nh[e],r=e!=="status"&&Array.isArray(t);return new x(r?`Invalid ${e} filter: pass it at most once (multiple values are not supported). Allowed: ${n}`:`Invalid ${e} filter. Allowed: ${n}`,"invalid_filter",{details:{parameter:e,allowed:n,hint:`Pass ${e} as ${n}, or omit it.`}})}function Fo(e,t){if(Array.isArray(t))throw Mn(e,t);return t}function rh(e){return typeof e=="number"?Number.isInteger(e)?e:null:typeof e=="string"&&/^-?\d+$/.test(e.trim())?Number(e.trim()):null}function Ri(e){return e==null||e===""}function oh(e){if(!Ri(e)){if(e==="true"||e==="1"||e===!0)return!0;if(e==="false"||e==="0"||e===!1)return!1;throw Mn("mine",e)}}function Ti(e){if(Ri(e))return;let t=(Array.isArray(e)?e:[e]).flatMap(r=>typeof r=="string"?r.split(","):[r]),n=[];for(let r of t){let o=typeof r=="string"?r.trim():r;if(o!==""){if(!th(o))throw Mn("status",e);n.includes(o)||n.push(o)}}return n.length>0?n:void 0}function Kd(e,t,n,r){if(Ri(t))return;let o=rh(t);if(o===null||o<n||o>r)throw Mn(e,t);return o}function Oo(e){let t={},n=oh(Fo("mine",e.mine));n!==void 0&&(t.mine=n);let r=Ti(e.status);r&&(t.status=r);let o=Fo("flowId",e.flowId??e.flow_id??e.flow);typeof o=="string"&&o.length>0&&(t.flowId=o);let s=Kd("limit",Fo("limit",e.limit),1,$n);s!==void 0&&(t.limit=s);let i=Kd("offset",Fo("offset",e.offset),0,Number.MAX_SAFE_INTEGER);return i!==void 0&&(t.offset=i),t}function rn(e){let t=e.offset+e.returned,n=t<e.total;return{total:e.total,offset:e.offset,...e.limit!==null?{limit:e.limit}:{},hasMore:n,...n?{nextOffset:t}:{}}}function sh(e,t){let n=e.length,r=t.offset!==void 0&&t.offset>0?t.offset:0;if(t.limit===void 0)return{items:r>0?e.slice(r):e,total:n,limit:null,offset:r};let o=Math.min(Math.max(1,t.limit),$n);return{items:e.slice(r,r+o),total:n,limit:o,offset:r}}function Dn(e,t,n={},r={}){let o=r.mine===!0?t.id:void 0,s={};o&&(s.linkId=o),r.flowId&&(s.flowId=r.flowId);let d=e.list(s).filter(c=>r.mine!==!1||!nn(c,t)).filter(c=>Si(c.status,r.status)).map(c=>({...ce(nt(c),n),mine:nn(c,t)})),l=sh(d,r);return{proposals:l.items,total:l.total,limit:l.limit,offset:l.offset}}function bt(e,t,n,r={}){if(!n)throw new x("Missing proposal id","missing_proposal_id");let o=e.get(n);return{...ce(nt(o),r),mine:nn(o,t)}}function ih(e){let t={kind:"access-link",linkId:e.id};return typeof e.label=="string"&&e.label&&(t.label=e.label),t}function Hn(e,t,n,r,o,s={}){if(!r)throw new x("Missing proposal id","missing_proposal_id");let i;try{i=e.get(r)}catch(d){throw d instanceof ye,d}if(!nn(i,n))throw new _t(r);if(!U(i.status))throw new Q(r,i.status);return{...Pi(e,t,ih(n),r,o,s),mine:!0}}function Pi(e,t,n,r,o,s={}){let i=e.get(r);if(n.kind==="access-link"&&mt(i.createdBy)!==n.linkId)throw new _t(r);let d=typeof o=="string"&&o.trim()?o.replace(/\0/g,"").trim().slice(0,2e3):n.kind==="admin"?"Withdrawn by administrator (token recovery)":"Withdrawn by access link",l=e.markWithdrawn(r,n,d),c=ce(nt(l),s);return t.append("proposal.withdrawn",n,{proposalId:r,flowId:i.payload.flowId,title:c.title,displayName:c.displayName,reason:d,withdrawalAuthority:n.kind==="admin"?"admin-recovery":"owner-token",originalOwnerId:mt(i.createdBy),...c.flowLabel?{flowLabel:c.flowLabel}:{}}),c}function ah(e,t,n,r={}){let o;try{o=e.get(t.id)}catch{return{}}if(r.live){ze(e,r.live,{flowId:o.payload.flowId});try{o=e.get(t.id)}catch{return{}}}return U(o.status)?at(it(e,o,n)):{}}function be(e,t,n,r={},o={}){let s="mine"in n?n:{...ce(n,r),mine:nn(n,t)},i=0;if(o.live)try{let c=e.get(s.id).payload.flowId;i=ze(e,o.live,{flowId:c}).expired;let u=e.get(s.id);s={...ce(u,r),mine:nn(u,t)}}catch{}let d=ue(nt(s),r),l={...d,mine:s.mine,...me(i)};return o.includeConflicts!==!1&&U(d.status)&&Object.assign(l,ah(e,d,t.id)),l}async function vt(e){let t=await e.getFlows();return{rev:t.rev,flows:t.flows}}async function Ge(e,t){if(!t)throw new x("Missing flow id","missing_flow_id");let n=await e.getFlow(t);return{rev:n.rev,flow:_n(t,n.flow)}}function dh(e){if(typeof e.expectedRev=="string"&&e.expectedRev.length>0)return e.expectedRev;if(typeof e.expected_rev=="string"&&e.expected_rev.length>0)return e.expected_rev}function Vd(e,t){if("nodes"in e&&e.nodes!==void 0&&!Array.isArray(e.nodes))throw new x("payload.nodes must be an array of node ops","invalid_nodes");if(t!==void 0&&!Array.isArray(t))throw new x("nodes must be an array of node ops","invalid_nodes")}async function lh(e,t,n,r,o){let{applyFlowNodeOps:s,interpretPatch:i}=await Promise.resolve().then(()=>(No(),Gd));Vd(n,r);let d=Array.isArray(n.nodes)?n.nodes:Array.isArray(r)?r:null;if(!d)throw new x("payload.flow is required","missing_flow");let l=n.flowId,c=typeof l=="string"?l:"";if(!c)throw new x("payload.flowId is required","missing_flow_id");let u=i({nodes:d,flowId:c},c);if(u.kind!=="nodes")throw new x("payload.nodes must be node ops","invalid_patch");let p=await s(e,u.flowId,u.ops,{expectedRev:t,...o?{nodeTypes:o}:{}});if(p.expectedRev!==t)throw new re(t,p.expectedRev,"submit_proposal");return{flowId:u.flowId,flow:p.flow}}async function ch(e,t,n,r,o){let s=n.flowId,i=n.flow,d=!i||typeof i!="object"||Array.isArray(i);if(d&&Vd(n,r),d&&(Array.isArray(n.nodes)||Array.isArray(r))){let l=await lh(e,t,n,r,o);s=l.flowId,i=l.flow}if(typeof s!="string"||s.length===0)throw new x("payload.flowId is required","missing_flow_id");if(!i||typeof i!="object"||Array.isArray(i))throw new x("payload.flow is required","missing_flow");return{flowId:s,flow:i}}function uh(e,t){try{return{safeExpectedRev:Qe(e.slice(0,200),"expectedRev"),safeFlowId:Qe(t.slice(0,200),"flowId")}}catch{throw new x("expectedRev and payload.flowId must be safe path segments","invalid_path_segment")}}function ph(e,t){let n=wd(Ue(e),t);return n.unresolved.length>0&>(n.unresolved.map(r=>({code:"redacted_placeholder_mismatch",error:`Node ${r.nodeId} field ${r.property}: ${r.reason}. [REDACTED] placeholders are restored only when left exactly where they were shown (same node, field, env/header name, and unchanged line). Do not copy, move, or add placeholders; keep those lines unchanged or supply a real new value.`,nodeId:r.nodeId,property:r.property,statusCode:400}))),n}async function Yd(e,t,n){let r=e.type,o=dh(e),s=e.payload;if(r!=="replace_flow")throw new x("type must be 'replace_flow'","invalid_proposal_type");if(!o)throw new x("expectedRev is required (top-level field next to type and payload)","missing_expected_rev");if(!s||typeof s!="object"||Array.isArray(s))throw new x("payload must be an object","invalid_payload");let i=s,d=we(t),l=await d.getFlows(),c=yo(l,d.kind);if(l.rev!==o){let T=js(d)?.get(o);if(!T)throw new re(o,l.rev,"submit_proposal");c=yo(T,d.kind)}let u=await ch(Lt(c),o,i,e.nodes,n),{safeExpectedRev:p,safeFlowId:f}=uh(o,u.flowId),h=u.flow,m=Buffer.byteLength(JSON.stringify(s),"utf8");if(m>Y)throw new he("proposal payload",m,Y);gt(Xa(h,f));let w=await c.getFlows();if(w.rev!==p)throw new re(p,w.rev,"submit_proposal");let v=ph(h,w.flows),k=Ue(v.flow);gt(Za(k,f,w.flows));let $=await c.getFlow(f);if($.rev!==p)throw new re(p,$.rev,"submit_proposal");let C=Ue($.flow);n&&await Ja(n,k);let{errors:O,warnings:q}=await Qa(k,{flowId:f,baseFlow:C,...n?{nodeTypes:n}:{}});return gt(O),q.unshift(...Tr(C,k).map(T=>({...T})),...kd(v.restoredFields,k,w.flows).map(T=>({...T}))),{safeExpectedRev:p,safeFlowId:f,safeFlow:k,safeBaseFlow:C,restoration:v,flowWarnings:q,head:l}}function xi(e){let t={kind:"access-link",linkId:e.id};return e.label!==void 0&&(t.label=e.label),t}async function Ot(e,t,n,r,o,s={},i){let d=await Yd(r,o,i),{safeExpectedRev:l,safeFlowId:c,safeFlow:u,safeBaseFlow:p,restoration:f,flowWarnings:h}=d,m=xi(n),g=n.oauthClientId&&n.oauthUser?{kind:"oauth",clientId:n.oauthClientId,user:n.oauthUser}:m,w=vr(r.title)??ri(p,u),v=e.create({type:"replace_flow",expectedRev:l,payload:{flowId:c,flow:u},baseFlow:p,...f.restoredFields.length>0?{restoredSecrets:f.restoredFields}:{},title:w,createdBy:g,...h.length>0?{warnings:h}:{}}),k=We(e,v,d.head).proposal,$=ce(nt(k),s),C=$.flowLabel||void 0;return t.append("proposal.created",m,{proposalId:k.id,flowId:c,expectedRev:l,...d.head.rev!==l?{liveRev:d.head.rev}:{},...k.rebase?{rebaseState:k.rebase.state}:{},title:$.title,displayName:$.displayName,...C?{flowLabel:C}:{},...h.length>0?{warningCount:h.length}:{},...f.restored>0?{restoredRedactions:f.restored}:{}}),$}async function $o(e,t,n,r,o,s,i={},d){if(!r)throw new x("Missing proposal id","missing_proposal_id");let l=e.get(r);if(!nn(l,n))throw new _t(r);if(!U(l.status))throw new Q(r,l.status);if(o.choice!==void 0||o.nodeId!==void 0){if(o.payload!==void 0||o.nodes!==void 0||o.expectedRev!==void 0||o.title!==void 0)throw new x("Use nodeId + choice alone, or expectedRev + payload to replace the body; do not mix the modes.","invalid_choice");let m=await _i(e,t,xi(n),r,o.nodeId,o.choice,s);return{...ce(nt(m),i),mine:!0}}let c=await Yd({type:"replace_flow",...o,payload:o.payload??{flowId:l.payload.flowId}},s,d);if(c.safeFlowId!==l.payload.flowId)throw new x(`payload.flowId must stay '${l.payload.flowId}' (submit a new proposal for another flow)`,"flow_id_mismatch");let u=vr(o.title),p=e.updateBody(r,{baseFlow:c.safeBaseFlow,flow:c.safeFlow,expectedRev:c.safeExpectedRev,restoredSecrets:c.restoration.restoredFields,warnings:c.flowWarnings,...u?{title:u}:{}}),f=We(e,p,c.head).proposal,h=ce(nt(f),i);return t.append("proposal.resolved",xi(n),{proposalId:r,flowId:f.payload.flowId,expectedRev:c.safeExpectedRev,...c.restoration.restored>0?{restoredRedactions:c.restoration.restored}:{},title:h.title,displayName:h.displayName,...f.rebase?{rebaseState:f.rebase.state}:{}}),{...h,mine:!0}}async function _i(e,t,n,r,o,s,i){if(s!=="live"&&s!=="proposed")throw new x("choice must be 'live' or 'proposed'","invalid_choice");if(typeof o!="string"||o.length===0||o.length>200)throw new x("nodeId is required","missing_node_id");let d=await we(i).getFlows(),l=e.get(r);if(!U(l.status))throw new Q(r,l.status);let c=We(e,l,d).proposal,u=c.rebase?.conflicts.find(m=>m.nodeId===o);if(!u)throw new x("No current conflict for that node (it may already be resolved). Reload the proposal.","conflict_not_found",{statusCode:409});if(!u.choices.includes(s))throw new x("That choice is not available for this conflict.","invalid_choice");let p;try{p=Cd({flowId:c.payload.flowId,base:c.baseFlow,proposed:c.payload.flow,liveFlows:d.flows,nodeId:o,choice:s})}catch(m){throw m instanceof Sr?new et(r,c.rebase?.conflicts??[]):new x("This conflict cannot be resolved by hand.","invalid_choice")}let f=e.updateBody(r,{baseFlow:Ue(p.base),flow:Ue(p.proposed),conflictChoice:{nodeId:o,choice:s,chooser:n.kind==="admin"?"human":"ai",undo:{baseFlow:c.baseFlow??{},flow:c.payload.flow,warnings:c.warnings??[],aiConflictOverrides:c.aiConflictOverrides??[]}},aiConflictOverrides:[...(c.aiConflictOverrides??[]).filter(m=>m.nodeId!==o),...n.kind!=="admin"&&s==="proposed"?[{nodeId:o,fields:u.fields?.length?u.fields:[""]}]:[]],warnings:[...(c.warnings??[]).filter(m=>m.code!=="secret_may_revert"||m.nodeId!==o),...p.warnings]}),h=We(e,f,d).proposal;return t.append("proposal.conflict_resolved",n,{proposalId:r,flowId:h.payload.flowId,nodeId:o,choice:s,kind:u.kind,chooser:n.kind==="admin"?"human":"ai",status:h.status}),h}async function Lr(e,t){let n=we(e),r;try{r=(await n.getFlows()).rev}catch{return e}if(r===t)return e;let o=js(n)?.get(t);if(!o)return e;let s=yo(o,n.kind);return e===n?s:Lt(s)}function J(e,t="rest"){return Nr(e,t)}var $n,Jd,nh,x,le=W(()=>{"use strict";y();Ee();mo();Us();tt();co();de();ct();ei();Zt();en();qe();On();lt();Ne();a(nn,"isMineProposal");$n=200,Jd=new Set(["pending","conflict","conflict","approved","rejected","failed","withdrawn","expired","superseded"]);a(Si,"matchesStatusFilter");a(th,"isProposalStatus");nh={status:[...Jd].join(", "),limit:`integer 1\u2013${$n}`,offset:"integer \u2265 0",mine:"true, false, 1, 0",flowId:"one flow tab id"};a(Mn,"invalidFilterError");a(Fo,"singleQueryValue");a(rh,"parseIntegerFilter");a(Ri,"isUnset");a(oh,"parseMineFilter");a(Ti,"parseStatusFilter");a(Kd,"parseRangeFilter");a(Oo,"parseListProposalsQuery");a(rn,"listPageFields");a(sh,"applyListPagination");a(Dn,"listAccessibleProposals");a(bt,"getAccessibleProposal");a(ih,"accessLinkActor");a(Hn,"withdrawMyProposal");a(Pi,"withdrawProposalAsActor");a(ah,"conflictsForProposal");a(be,"presentAccessibleProposalForApi");x=class extends Error{static{a(this,"AiOpsValidationError")}statusCode;code;details;constructor(t,n="validation_error",r={}){super(t),this.name="AiOpsValidationError",this.code=n,this.statusCode=r.statusCode??400,r.details&&(this.details=r.details)}};a(vt,"listFlows");a(Ge,"getFlow");a(dh,"resolveExpectedRev");a(Vd,"assertNodesArrayOrAbsent");a(lh,"materializePayloadFromNodes");a(ch,"resolveReplaceFlowPayload");a(uh,"assertSafeRevAndFlowId");a(ph,"strictRestoreOrThrow");a(Yd,"prepareReplaceFlowSubmission");a(xi,"auditActorFor");a(Ot,"submitReplaceFlowProposal");a($o,"resolveMyProposal");a(_i,"resolveConflictChoice");a(Lr,"adapterAtRev");a(J,"aiOpsErrorToHttp")});function Ci(e){return e==="get_link"?86400:2592e3}function Ei(e,t){return e==="get_link"?`get_link ttlSeconds must be \u2264 ${t} (24 hours)`:`ttlSeconds must be \u2264 ${t} (30 days) for ${e} tokens`}var on,Fr=W(()=>{"use strict";y();a(Ci,"maxTtlSecondsForKind");a(Ei,"ttlCapErrorMessage");on="ttlSeconds must be a positive number of seconds within the cap for the token kind: get_link \u2264 86400 (24h); rest and mcp \u2264 2592000 (30 days)."});function _(e,t,n,r,o=ie,s="error"){return{code:e,status:t,meaning:n,hint:r,channels:o,kind:s}}function Or(e){return hh.get(e)?.hint}function Zd(e){return e.replace(/\\/g,"\\\\").replace(/\|/g,"\\|")}function Xd(){let e=a(s=>`| \`${s.code}\` | ${s.status} | ${Zd(s.meaning)} | ${Zd(s.hint)} | ${s.channels.join(", ")} |`,"row"),t=["| Code | HTTP | Meaning | Next step | Channels |","| --- | --- | --- | --- | --- |"],n=Mo.filter(s=>s.kind==="error"),r=Mo.filter(s=>s.kind==="warning"),o=Mo.filter(s=>s.kind==="status");return[...t,...n.map(e),"","**Warnings** (in `warnings[]` on a successful propose; never blocking):","",...t,...r.map(e),"","**Proposal statuses.** Open proposals are closed automatically: `expired` after the configured TTL (default 7 days). Revision changes auto-rebase open proposals; `conflict` blocks approval until resolved. Legacy closed proposals stay closed. Submit, get, and list responses include `autoClosedCount` when that request auto-closed proposals (omitted when zero).","",...t,...o.map(e)].join(`
|
|
10
|
+
`)}var ie,jn,Un,fh,kt,Mo,hh,Do=W(()=>{"use strict";y();Fr();ie=["GET","REST","MCP"],jn=["GET"],Un=["REST","MCP"],fh=["GET","REST"],kt=["admin"];a(_,"e");Mo=[_("duplicate_env_keys",200,"Changed env arrays contain duplicate names; pairing by name is ambiguous when entries are reordered","Review the env values before approval",[...ie,"admin"],"warning"),_("approve_queue_timeout",504,"Approval exceeded its queue-wait deadline (default 60 seconds); no deployment was started for this request","Check the live flow and retry approval",kt),_("deploy_outcome_unknown",409,"A past-cap deployment can still change live flows","All approvals require settlement, confirm live checked with the matching attempt, restart, or an audited override; rejection is allowed with a warning",kt),_("deploy_timeout",504,"Deployment exceeded its deadline (default 60 seconds); approvals wait for settlement or the hard cap (default 5 minutes)","Before the cap, late settlement updates the proposal; after the cap, deployPending.queueReleased is true and the outcome stays unknown. Check the live flow before approving again; approval always re-reads live",kt),_("secret_may_revert",200,"Secret spans could not be aligned during proposed conflict resolution; proposed values were kept","Review the warned field against live flows before approval; an editor secret change may be reverted",[...ie,"admin"],"warning"),_("proposal_conflict",409,"Editor and proposal changes conflict (conflicts contains field names only)","Read the current flow and resolve the proposal before approval",[...ie,"admin"]),_("rebase_ack_required",409,"The merged result needs review (mergedHash included)","Review the diff and approve with its mergedHash",["admin"]),_("rebase_race",409,"Flows kept changing during approval","Recheck the proposal and retry approval",["admin"]),_("conflict_not_found",409,"The requested conflict no longer exists","Reload the proposal and its conflicts",[...Un,"admin"]),_("invalid_choice",400,"Invalid manual conflict choice","Use nodeId plus an available live or proposed choice, without replacement payload or revision",[...Un,"admin"]),_("flow_id_mismatch",400,"Resolution tried to change the proposal flow id","Keep the same flowId or submit a new proposal",Un),_("access_link_missing",401,"No Bearer / path token","Ask the user for a new access link of the right kind"),_("bearer_scheme_required",401,"Auth used Basic (or non-Bearer)","Send `Authorization: Bearer <token>`",Un),_("token_expired",401,"Known token past `expiresAt` (body includes `expiredAt` and `accessLinksUrl`)","Ask the user to create a new link on the `/ai-access` dashboard \u2192 Access tokens (`accessLinksUrl`)"),_("access_link_invalid",401,"Unknown, revoked, or cleared after restart","Ask the user for a new access link"),_("wrong_token_kind",403,"Token kind does not match this surface","Mint the matching kind on `/ai-access` \u2192 Access tokens; show `tellUser` to the human"),_("ai_features_disabled",403,"Plugin AI features off","Owner enables AI Access / `adminAuth`"),_("auth_required",401,"Admin session required","Sign in to the Node-RED editor",kt),_("insufficient_permissions",403,"Admin lacks required scope","Need `*` for link mint / approve",kt),_("invalid_link_kind",400,"Unknown `kind` on create","Use `get_link`, `rest`, or `mcp`",kt),_("invalid_ttl",400,"`ttlSeconds` is zero, negative, not a number, or over the cap for the kind",on,kt),_("access_link_limit",409,"Too many active links","Revoke an existing link first",kt),_("access_link_not_found",404,"Unknown link id on revoke","List links and use an existing id",kt),_("link_create_not_supported",403,"Attempted mint under a GET link","Admin mints via `POST /mcp/api/v1/links` only",jn),_("rate_limited",429,"Per-IP or per-token limit","Wait `retryAfter` / `Retry-After`"),_("payload_too_large",413,"Body / proposal over ~1.5MB","Shrink content"),_("invalid_json",400,"Malformed JSON body","Fix Content-Type / body",Un),_("unsupported_media_type",415,"POST body is not `application/json`","Send `Content-Type: application/json`",Un),_("url_too_long",414,"GET URL over soft cap (~8KB)","Use chunked drafts or REST/MCP",jn),_("not_found",404,"Unknown path or resource","Re-read docs / discovery"),_("missing_query",400,"Required search `q` missing","Pass `?q=`",fh),_("invalid_filter",400,"Invalid list filter (`status`, `limit`, `offset`, `mine`); body names `parameter` and `allowed`","Use an allowed value: status one of the proposal statuses; limit integer 1\u2013200; offset integer \u2265 0; mine true|false"),_("invalid_arguments",400,"MCP tool arguments failed schema validation; body names `parameter`","Fix the named argument to match the tool input schema (see tools/list)",["MCP"]),_("invalid_diff_format",400,"Unknown `format` on proposal diff","Use `format=summary` (default) or `format=full`"),_("confirm_required",400,"GET withdraw without `confirm=1`","Repeat the request with `confirm=1`",jn),_("missing_expected_rev",400,"Proposal submitted without `expectedRev` / `rev`","Read the flow and pass its `rev`"),_("version_mismatch",409,"Unknown or unavailable `rev` / `expectedRev` snapshot","Re-read flows and resubmit"),_("proposal_not_found",404,"Unknown proposal id","List proposals and use an existing id"),_("proposal_not_pending",409,"Action needs an open proposal","Only pending or conflict proposals can withdraw / decide"),_("proposal_not_owned",403,"Resolve or withdraw another token\u2019s proposal","Use the creating token; lost-token withdrawal requires a full admin session"),_("validation_failed",400,"Several validation errors at once (message `N validation errors`)","Fix every entry in errors[] and resubmit."),_("duplicate_node_id",400,"The same node id appears more than once in a full tab or batch","Give every node a unique id"),_("missing_node_id",400,"Node / op without an id","Include a non-empty string id"),_("missing_node_type",400,"Node without a `type`","Set `type` to an installed palette type name"),_("invalid_node_type",400,"`type` is not a string","Use the palette type name exactly"),_("invalid_nodes",400,"`flow.nodes` / `flow.configs` not an array","Send arrays (use `[]` to clear)"),_("invalid_node",400,"Node entry is not an object","Send `{ id, type, \u2026 }` objects"),_("invalid_wires",400,"Bad wires shape, missing target, or more ports than outputs","Wire only to existing node ids / ports"),_("z_mismatch",400,"Node `z` differs from the flow id","Set `z` to the tab id or omit it"),_("node_already_exists",400,"`add` op for an id already in the flow","Use `set` or a new id"),_("protected_property",400,"Tried to change id / type / z","Omit id, type, and z from edits"),_("node_not_found",404,"Unknown node (single error only; several errors \u2192 `validation_failed`)","Re-read the flow"),_("field_not_found",404,"Unknown field on a node","Re-read the node"),_("text_not_found",400,"`replace_text` find had no match","Re-read field; find is exact"),_("ambiguous_match",409,"`replace_text` matched more than once","Pass `occurrence` (1-based) or unique find"),_("unknown_node_type",400,"Proposal uses a type that is not installed","List node-types / palette search; ask user to install"),_("node_type_not_found",404,"Unknown type on node-types detail","List types or search palette"),_("chunk_conflict",409,"Same draft seq re-sent with different data","Re-send identical data or start a new draft",jn),_("missing_chunks",400,"Draft submit with gaps in seq","Upload every seq from 0 through max",jn),_("draft_hash_mismatch",400,"Draft sha256 does not match assembled bytes","Send the hex sha256 of the assembled UTF-8",jn),_("missing_config_node",201,"Warning: node has no (or an unresolved) config node yet","Configure before deploy if needed; see `candidates`",ie,"warning"),_("unwired_node",201,"Warning: node has outputs but no wires","Wire it, or ignore while scaffolding",ie,"warning"),_("incomplete_wires",201,"Warning: fewer wire ports than outputs","Add ports if outputs should connect",ie,"warning"),_("redacted_placeholder_mismatch",400,"A `[REDACTED]` placeholder was added, duplicated, moved, or its env/header name changed","Keep placeholders exactly where they were shown (same line, same name) or supply a real value",ie),_("secret_destination_changed",201,"Warning: a node holding a restored secret changed where it sends data (url/host/method/headers/\u2026)","Reviewer must verify the new destination before approving",ie,"warning"),_("secret_node_code_changed",201,"Warning: a node holding a restored secret had its code changed","Reviewer must check the code does not send the secret elsewhere",ie,"warning"),_("secret_wires_changed",201,"Warning: a node holding a restored secret had its output wires changed","Reviewer must check where its messages now go",ie,"warning"),_("secret_scope_changed",201,"Warning: tab/group env or a config node scoping a secret-holding node changed","Reviewer must check env-resolved hosts/URLs (e.g. `${API_HOST}`)",ie,"warning"),_("outbound_node_changed",201,"Warning: proposal adds/changes an outbound-capable node (http request/response, mqtt, tcp, udp, email, websocket, link out/call, function, exec, file, or any node with url/host fields)","Reviewer must check it cannot send secret data elsewhere",ie,"warning"),_("secret_tab_changed",201,"Warning: proposal with restored secrets also changes other nodes in the tab","Review every change before approving",ie,"warning"),_("secret_reference_added",201,"Warning: an added/changed node now reads a secret-named env/global/flow value (env.get, $env, ${\u2026}, typed input)","Reviewer must check the value is not sent anywhere unexpected",ie,"warning"),_("conflict",200,"Status: open proposal has conflicting editor changes; approval blocked","Read the current flow and resolve the proposal",ie,"status"),_("expired",200,"Status: open proposal expired after the configured TTL (default 7 days)","Re-read the flow and submit a new proposal",ie,"status"),_("superseded",200,"Legacy closed status; new approvals rebase other open proposals","Re-read the flow and submit a new proposal if still needed",ie,"status"),_("upstream_error",502,"Node-RED runtime/flows error","Retry; check Node-RED logs"),_("internal_error",500,"Unexpected server error","Retry; check Node-RED logs")],hh=new Map(Mo.map(e=>[e.code,e]));a(Or,"registryHint");a(Zd,"cell");a(Xd,"renderGetLinkErrorTable")});function Ho(e){let t=ce(e),n=xo(e)||void 0;return{proposalId:e.id,title:t.title,displayName:t.displayName,flowId:e.payload.flowId,...n?{flowLabel:n}:{}}}function Ii(e,t){return e!==void 0&&Number.isFinite(e)&&e>0?e:t}var el,sn,an,$t,dn,At,jo,mh,Qd,Uo,Bo=W(()=>{"use strict";y();el=require("crypto");Ee();Ps();tt();qe();On();lt();Ne();a(Ho,"auditProposalMeta");sn=class extends Error{static{a(this,"ProposalRebaseAckRequiredError")}code="rebase_ack_required";statusCode=409;mergedHash;rebaseState;constructor(t,n,r){super(`Proposal '${t}' was rebased onto newer editor changes since you viewed it. Review the updated diff, then approve again with the new mergedHash.`),this.name="ProposalRebaseAckRequiredError",this.mergedHash=n,this.rebaseState=r}},an=class extends Error{static{a(this,"ProposalRebaseRaceError")}code="rebase_race";statusCode=409;constructor(t){super(`Proposal '${t}' could not be applied because the flows kept changing during approve. It is still pending \u2014 try again.`),this.name="ProposalRebaseRaceError"}},$t=class extends Error{static{a(this,"ProposalDeployTimeoutError")}code="deploy_timeout";statusCode=504;constructor(){super("Deployment timed out; the deploy outcome is unknown. The proposal remains pending with deployPending set. Approvals wait until the runtime settles or the deploy hard cap is reached. Check the live flow before approving again."),this.name="ProposalDeployTimeoutError"}},dn=class extends Error{static{a(this,"ProposalApproveQueueTimeoutError")}code="approve_queue_timeout";statusCode=504;constructor(){super("Approval timed out waiting in the queue. No deployment was started for this request. Check the live flow and retry."),this.name="ProposalApproveQueueTimeoutError"}},At=class extends Error{static{a(this,"ProposalDeployOutcomeUnknownError")}code="deploy_outcome_unknown";statusCode=409;constructor(){super("A past-cap deployment can still change live flows. Wait for settlement or explicitly override after reviewing the risk.")}},jo=class extends Error{static{a(this,"DeployHardCapError")}},mh=3;a(Ii,"deadlineMs");Qd=Promise.resolve(),Uo=class{constructor(t,n,r,o,s={}){this.proposals=t;this.audit=n;this.adapter=r;this.getDeploymentType=o;this.serviceOptions=s;fa(we(r))}static{a(this,"ProposalService")}unknownDeployBlocks(){return this.proposals.list().filter(t=>t.deployPending?.queueReleased)}confirmLiveChecked(t,n,r){let o=this.proposals.get(t),s=o.deployPending?.attemptId;if(!o.deployPending?.queueReleased||!s||r!==s)throw new At;return this.audit.append("proposal.live_checked",n,{...Ho(o),attemptId:s}),this.proposals.recordDeployPending(t,void 0)}guardUnknown(t=!1){let n=this.unknownDeployBlocks();if(!t&&n.length)throw new At;return n}async approve(t,n,r={}){let o=this.proposals.get(t);this.guardUnknown(r.overrideDeployOutcomeUnknown);let s=o.deployPending!==void 0;if(!U(o.status))throw new Q(t,o.status);if(!s&&this.proposals.isApproving(t))throw new Le(t);let i=Qd,d,l=new Promise(c=>{d=c});return Qd=i.then(()=>l),new Promise((c,u)=>{let p=!1,f,h=new Promise((g,w)=>{f=setTimeout(()=>w(new dn),Ii(this.serviceOptions.approveQueueTimeoutMs,6e4)),f.unref?.()});a(async()=>{try{await Promise.race([i,h]),clearTimeout(f);let g=this.proposals.get(t);if(!U(g.status)&&g.status!=="approved")throw new Q(t,g.status);let w=this.guardUnknown(r.overrideDeployOutcomeUnknown);if(r.overrideDeployOutcomeUnknown&&w.length&&this.audit.append("proposal.deploy_unknown_override",n,{...Ho(g),skippedAttempts:w.map(k=>({proposalId:k.id,attemptId:k.deployPending?.attemptId,flowId:k.payload.flowId}))}),s&&g.status==="approved")return g;let v=this.proposals.beginApprove(t);return p=!0,await this.approveLocked(v,n,r,u)}finally{clearTimeout(f),p&&this.proposals.releaseLock(t),d()}},"run")().then(c,u)})}async approveLocked(t,n,r,o){let s=t.id,i,d=Ho(t),l=r.deploymentType??this.getDeploymentType(),c=t.type;if(c!=="replace_flow"){let f=this.proposals.markFailed(s,n,`Unsupported proposal type '${c}'`);return this.audit.append("proposal.failed",n,{...d,reason:f.reason}),f}if(Lo(t))throw this.proposals.releaseLock(s),se(this.proposals,{flowId:t.payload.flowId}),new Q(s,this.proposals.get(s).status);let u=we(this.adapter),p=a((f,h)=>{let m=this.proposals.markApproved(s,n,h);return this.audit.append("proposal.approved",n,{...d,resultRev:h,liveRev:h,baseRev:f.expectedRev,alreadyApplied:!0}),m},"markAlreadyApplied");for(let f=0;f<mh;f++){let h=await u.getFlows(),m=this.proposals.get(s),g=st(h.flows,m.payload.flowId,m.payload.flow),w=g&&wt(g);if(w&&(w===m.deployPending?.mergedHash||w===wt(m.payload.flow))){i=p(m,h.rev);break}let{result:v}=We(this.proposals,m,h,{duringApprove:!0});if(v.state==="conflict"||!v.merged||!v.mergedHash)throw this.audit.append("proposal.conflict",n,{...d,liveRev:h.rev,conflictCount:v.conflicts.length}),new et(s,vi(v).conflicts);if(w===v.mergedHash){i=p(m,h.rev);break}if((v.state==="merged"||r.mergedHash!==void 0)&&r.mergedHash!==v.mergedHash)throw new sn(s,v.mergedHash,v.state);let $=v.merged,C=v.mergedHash,O=(0,el.randomUUID)(),q=a(()=>{let P=this.proposals.get(s);return U(P.status)&&P.deployPending?.attemptId===O},"ownsOutcome");m.deployPending&&this.proposals.recordDeployPending(s,{...m.deployPending,attemptId:O});let T=!1,S=new Map,F=a(P=>JSON.stringify([P.status,P.decidedBy,P.reason,P.resultRev]),"decisionKey"),N=a(()=>{this.proposals.get(s).deployPending?.attemptId===O&&this.proposals.recordDeployPending(s,void 0)},"clearUnknown");try{let P=await this.withDeployDeadline(()=>u.updateFlow({flowId:m.payload.flowId,flow:$,expectedRev:h.rev,deploymentType:l}),()=>{T=!0;let A=new $t;this.proposals.recordDeployPending(s,{mergedHash:C,attemptId:O,reason:"Deploy outcome unknown: the runtime has not settled. Approvals are waiting."}),this.audit.append("proposal.deploy_timeout",n,{...d,code:A.code}),o(A)},()=>{S=new Map(this.proposals.list().map(A=>[A.id,F(A)])),this.proposals.recordDeployPending(s,{mergedHash:C,attemptId:O,queueReleased:!0,reason:"Deploy outcome unknown: the runtime did not settle before the hard cap. The approval queue was released. Check the live flow before approving again."}),o(new $t)},async A=>{let I=pa(u,A.rev)??h,b=await u.getFlows().catch(()=>{}),M=new Set,H=new Set([...I.flows,...b?.flows??[]].filter(G=>G.type==="tab").map(G=>G.id));for(let G of H){let Ve=st(I.flows,G,{}),qt=b&&st(b.flows,G,{});(!b||wt(Ve??{})!==wt(qt??{}))&&M.add(G)}let L=this.proposals.get(s),ee=L.status==="approved"&&b?.rev===A.rev&&L.resultRev!==void 0&&(L.rebase?.mergedHash===C||wt(L.payload.flow)===C)&&wt(st(b.flows,m.payload.flowId,$)??{})===C,Se=this.proposals.list().filter(G=>M.has(G.payload.flowId)&&!U(G.status)&&S.get(G.id)!==F(G));if(L.status==="withdrawn"&&this.proposals.flagLandedAfterWithdraw(s),Se.length||[...M].some(G=>G!==m.payload.flowId)){let G=`Late deployment of ${s} changed live tabs after the hard cap. Check live flows; intervening edits or decisions may have been overwritten.`;for(let Ve of Se)(Ve.id!==s||Ve.status!=="withdrawn")&&this.proposals.flagDeployment(Ve.id,G);U(L.status)&&this.proposals.flagDeployment(s,G),this.audit.append("proposal.late_deploy_overwrite",n,{...d,attemptId:O,affectedProposalIds:Se.map(Ve=>Ve.id),changedFlowIds:[...M],reason:G}),console.warn(G)}if(ee&&(this.proposals.markApproved(s,L.decidedBy??n,A.rev),this.audit.append("proposal.late_deploy_already_applied",n,{...d,attemptId:O,resultRev:A.rev})),!q()){N();return}this.proposals.markApproved(s,n,A.rev),this.audit.append("proposal.approved",n,{...d,resultRev:A.rev,baseRev:m.expectedRev,liveRev:h.rev,rebaseState:v.state})},()=>{N()});i=this.proposals.markApproved(s,n,P.rev),this.audit.append("proposal.approved",n,{...d,resultRev:P.rev,flowId:m.payload.flowId,baseRev:m.expectedRev,liveRev:h.rev,rebaseState:v.state});break}catch(P){if(i||P instanceof jo)throw P;if(T)throw this.proposals.recordDeployPending(s,void 0),P;if(P instanceof re)continue;let A=P instanceof Error?P.message:String(P),I=this.proposals.markFailed(s,n,A);return this.audit.append("proposal.failed",n,{...d,reason:A}),I}}if(!i)throw new an(s);try{await _r(this.proposals,this.adapter,{flowId:i.payload.flowId})}catch{}return i}async withDeployDeadline(t,n,r,o,s){let i=setTimeout(n,Ii(this.serviceOptions.deployTimeoutMs,6e4));i.unref?.();let d=!1,l,c=new Promise((u,p)=>{l=setTimeout(()=>{try{d=!0,r()}finally{p(new jo)}},Ii(this.serviceOptions.deployHardCapMs,3e5)),l.unref?.()});try{let u=t();return u.then(p=>{if(d)return o(p)},()=>{d&&s()}).catch(()=>{}),await Promise.race([u,c])}finally{clearTimeout(i),clearTimeout(l)}}reject(t,n,r){let o=this.proposals.get(t),s=this.unknownDeployBlocks();if(!U(o.status))throw new Q(t,o.status);let i=Ho(o),d=this.proposals.markRejected(t,n,r.trim()||"Rejected by admin");return s.length&&(d=this.proposals.flagRejectedWhileDeployUnknown(t)),this.audit.append("proposal.rejected",n,{...i,reason:d.reason,rejectedWhileDeployUnknown:d.rejectedWhileDeployUnknown}),d}}});function Ae(e,t,n={}){return Li(e,t,n)}function gh(e,t){return t==="excess_ports"?e==="mcp"?"This node has more wire ports than its type declares \u2014 remove extra ports or raise the node outputs, then retry.":e==="rest"?"This node has more wire ports than its type declares \u2014 trim wires[] or raise outputs, then retry.":"This node has more wire ports than its type declares \u2014 trim wires or raise outputs, then retry.":t==="missing_target"?e==="mcp"?"Wire targets must be node ids in this flow \u2014 add the node in the same patch or use get_flow ids.":e==="rest"?"Wire targets must be node ids in this flow \u2014 add the node in the same patch or use GET /flows/{id} ids.":"wires must list existing node ids (or add those nodes in the same patch).":e==="mcp"||e==="rest"?"Fix wires: targets must exist in this flow, and port count must not exceed the node outputs.":"wires must list existing node ids (or add those nodes in the same patch)."}function yh(e){return e==="mcp"?"Call list_node_types or search_palette. Ask the user to install via Manage palette, then recheck. This API never installs.":e==="rest"?"Call GET /node-types?q= or GET /palette/search?q=. Ask the user to install via Manage palette, then recheck. This API never installs.":"Call GET \u2026/node-types?q= or \u2026/palette/search?q=. Ask the user to install via Manage palette, then recheck. This API never installs."}function Mt(e,t,n,r){return e==="mcp"?t:e==="rest"?n:r}function Li(e,t,n={}){switch(e){case"version_mismatch":return Mt(t,"Re-read with list_flows/get_flow and retry with the current rev.","Re-read the flow and retry with the current rev.","Re-read the flow and retry with the current rev.");case"node_not_found":return Mt(t,"Call get_flow or get_node and use a node id from that response.","Call GET /flows/{id} and use a node id from that response.","Call GET \u2026/flows/<id> and use a node id from that response.");case"flow_not_found":return Mt(t,"Call list_flows to list valid ids.","Call GET /flows to list valid ids.","Use an id from GET ./flows.");case"proposal_not_found":return Mt(t,"Call list_proposals and use an id from that response.","List proposals and use an id from that response.","List proposals and use an id from that response.");case"proposal_not_owned":return"Use the creating access link to resolve or withdraw. If that token is lost, an admin with full permission can withdraw through POST /proposals/{id}/withdraw.";case"proposal_not_pending":return"Only open (pending or conflict) proposals can be withdrawn or rejected.";case"confirm_required":return"Add confirm=1 to the withdraw URL (required so link prefetchers cannot withdraw).";case"invalid_wires":return gh(t,n.cause);case"node_type_not_found":case"unknown_node_type":return yh(t);case"missing_node_type":return'Each add op needs node.type as a non-empty string (e.g. "inject").';case"invalid_line_range":return Mt(t,'Pass lines as "A" or "A-B" (1-based inclusive). Reversed ranges (end < start) are invalid.',"Use lines=A or lines=A-B (1-based inclusive). Reversed ranges (end < start) are invalid.","Use lines=A or lines=A-B (1-based inclusive). Reversed ranges (end < start) are invalid.");case"line_range_out_of_bounds":return"start is past the end of the field \u2014 check totalLines and request a lower lines range.";case"ambiguous_match":return"find matched more than once \u2014 pass occurrence=N (1-based) or a more unique find string.";case"missing_expected_rev":return Mt(t,"Pass expectedRev from list_flows/get_flow (top-level, next to type and payload).","Pass expectedRev from GET /flows or GET /flows/{id} (top-level, next to type and payload).","Pass rev from GET \u2026/flows or \u2026/flows/<id>.");case"missing_query":return Mt(t,"Pass a non-empty q string.","q query parameter is required.","q query parameter is required.");case"unsupported_media_type":return"Send Content-Type: application/json with a JSON body.";case"text_not_found":return"Re-read the node field; find must match exactly (case-sensitive).";case"field_not_found":return"Re-read the node; field must be an existing string property (template, func, \u2026).";case"field_not_string":return Mt(t,"This tool only returns string fields (template, func, \u2026). Use get_node for other property types.","This endpoint only returns string fields (template, func, \u2026). Use GET /nodes/{id} for other property types.","This endpoint only returns string fields (template, func, \u2026). Use GET /nodes/{id} for other property types.");default:return}}function Nr(e,t,n={}){let r=n.canonicalizeMessage!==!1,o=null;if(e instanceof et?o={statusCode:409,body:{error:e.message,code:e.code,conflicts:e.conflicts}}:e instanceof sn?o={statusCode:409,body:{error:e.message,code:e.code,mergedHash:e.mergedHash}}:e instanceof an||e instanceof $t||e instanceof dn||e instanceof Le?o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}:e instanceof x?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,...e.details??{}}}:e instanceof re?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,expectedRev:e.expectedRev,currentRev:e.currentRev}}:e instanceof te?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,flowId:e.flowId}}:e instanceof ye||e instanceof _t||e instanceof Q||e instanceof he?o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}:(e instanceof Ze||e instanceof je)&&(o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}),!o)return null;let s=o.body.code;if(r&&!Array.isArray(o.body.errors)){let i=ut[s];i&&(o.body.error=i)}if(typeof o.body.hint!="string"||!o.body.hint){let i=Li(s,t,{cause:o.body.cause})??Or(s);i&&(o.body.hint=i)}return o}function tl(e,t,n={}){let o={error:n.error??ut[t]??"Request failed",code:t,...n.details??{}},s=Li(t,e,{cause:o.cause})??Or(t);return s&&(o.hint=s),{statusCode:n.statusCode??400,body:o}}var ut,ct=W(()=>{"use strict";y();Ee();tt();de();le();Do();Bo();ut={proposal_conflict:"Proposal conflicts with current editor changes",rebase_ack_required:"Review and acknowledge the merged proposal",rebase_race:"Flows kept changing during approval",deploy_outcome_unknown:"A past-cap deployment may still change live flows. Wait for settlement or explicitly override after reviewing the risk.",deploy_timeout:"Deployment timed out; the deploy outcome is unknown. The proposal remains pending with deployPending set. Approvals wait until the runtime settles or the deploy hard cap is reached. Check the live flow before approving again.",approve_queue_timeout:"Approval timed out waiting in the queue. No deployment was started for this request. Check the live flow and retry.",conflict_not_found:"Conflict no longer exists",invalid_choice:"Invalid conflict choice",flow_id_mismatch:"Proposal flow id cannot change",node_not_found:"Node was not found",flow_not_found:"Flow was not found",field_not_found:"Field was not found",field_not_string:"Field is not a string",missing_query:"q query parameter is required",missing_flow_id:"Missing flow id",missing_node_id:"Missing node id",missing_field:"Missing field name",invalid_line_range:"lines must be A or A-B (1-based inclusive; end >= start)",line_range_out_of_bounds:"lines range is out of bounds",proposal_not_found:"Proposal was not found",proposal_not_owned:"Proposal was not created by this access link",proposal_not_pending:"Proposal is not pending",confirm_required:"Withdraw over GET requires confirm=1",unsupported_media_type:"Content-Type must be application/json",upstream_error:"Upstream request failed",internal_error:"Internal server error"};a(Ae,"channelErrorHint");a(gh,"invalidWiresHint");a(yh,"nodeTypeHint");a(Mt,"pickChannel");a(Li,"hintFor");a(Nr,"mapErrorForChannel");a(tl,"buildChannelError")});var nw={};la(nw,{ACCESS_LINK_INVALID_MESSAGE:()=>zt,ACCESS_LINK_MISSING_MESSAGE:()=>He,AccessLinkStore:()=>gn,EDITOR_SIDEBAR_ASSET_VERSION:()=>Wn,MAX_PROPOSAL_JSON_BYTES:()=>Y,SettingsStore:()=>An,acceptedMediaTypes:()=>uu,areAiFeaturesEnabled:()=>De,bindSettingsStore:()=>to,createPluginServices:()=>As,default:()=>tw,getStorageDir:()=>fs,isAdminAuthEnabled:()=>ae,isMcpStreamRequest:()=>pu,packageVersion:()=>qn,registerRoutes:()=>fu,renderEditorSidebarJs:()=>qo,renderEditorSidebarLoaderHtml:()=>sl,requireAccessLink:()=>Pn,resolveAiAccessSettings:()=>Me});module.exports=vu(nw);y();var cu=require("fs");Ps();y();var Gr=require("fs"),sr=require("path");de();function Kr(e){let t=(0,sr.join)(e,".ai-access");return{root:t,proposals:(0,sr.join)(t,"proposals"),logs:(0,sr.join)(t,"logs"),settings:(0,sr.join)(t,"config.json")}}a(Kr,"resolveAiAccessPaths");function Cs(e){(0,Gr.mkdirSync)(e,{recursive:!0,mode:_s});try{(0,Gr.chmodSync)(e,_s)}catch{}}a(Cs,"mkdirSecure");function ya(e){Cs(e.root),Cs(e.proposals),Cs(e.logs)}a(ya,"ensureAiAccessDirs");Gt();y();Ye();function so(e){let t=Pe(e);return`auth-tokens${(t==="/"?"":t).replace(/\//g,"-")}`}a(so,"editorAuthTokenKey");Ye();y();ct();function wh(e){if(!e)return!1;let t=e.split(";")[0]?.trim().toLowerCase()??"";return t==="application/json"||t.endsWith("+json")}a(wh,"isJsonContentType");function bh(e){let t=e.headers["content-length"];if(typeof t=="string"&&t.length>0){let n=Number(t);if(Number.isFinite(n)&&n>0)return!0;if(n===0)return!1}return e.headers["transfer-encoding"]!==void 0}a(bh,"hasRequestBody");function Fe(e="rest"){return(t,n,r)=>{let o=t.headers["content-type"];if(wh(o)){r();return}if(!o&&!bh(t)){r();return}let s=tl(e,"unsupported_media_type",{statusCode:415});n.status(s.statusCode).json(s.body)}}a(Fe,"requireJsonContentType");var vh="text/plain; charset=utf-8",kh="text/html; charset=utf-8",Ah="default-src 'none'; base-uri 'self'; form-action 'none'; frame-ancestors 'none'";function xh(e){let t=e.query?.format;return typeof t=="string"?t.toLowerCase():void 0}a(xh,"formatParam");function Sh(e){let t=xh(e);if(t==="md"||t==="json")return"native";if(!e.get("accept"))return"html";let n=e.accepts(["text/html","application/json","text/markdown","text/plain"]);return n==="application/json"||n==="text/markdown"?"native":n==="text/plain"?"plain":"html"}a(Sh,"chooseGetLinkRepresentation");function nl(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}a(nl,"escapeHtml");var Rh=/(?<![A-Za-z0-9_.~\-/])\.\/[A-Za-z0-9._~\-/]+(?:\?[A-Za-z0-9._~\-=&%]+)?/g;function Th(e){return e.replace(Rh,(t,n)=>{if(e.startsWith("<",n+t.length))return t;let r=t.replace(/[.]+$/,""),o=t.slice(r.length);return r.length<=2||r.includes("..")||r.includes("//")||r.includes("&")?t:`<a href="${r}">${r}</a>${o}`})}a(Th,"linkifyEscaped");function Ph(e,t){return`<!doctype html>
|
|
11
|
+
<html><head><meta charset="utf-8">${t?`<base href="${nl(t)}">`:""}<title>Node-RED AI Access</title></head><body><pre>${Th(nl(e))}</pre></body></html>
|
|
12
|
+
`}a(Ph,"renderGetLinkHtml");function _h(e){let t=e.split("?")[0]??"",n=/^(.*?\/mcp\/a\/[A-Za-z0-9_-]+)(?:\/|$)/.exec(t);if(!(!n?.[1]||!/^[A-Za-z0-9._~\-/]*$/.test(n[1])))return`${n[1]}/`}a(_h,"getLinkBaseHref");function Ch(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}a(Ch,"prettyJson");function rl(){return(e,t,n)=>{let r=Sh(e),o=_h(e.originalUrl);t.vary("Accept"),t.setHeader("Cache-Control","no-store"),t.setHeader("Referrer-Policy","no-referrer"),t.setHeader("X-Robots-Tag","noindex, nofollow"),t.setHeader("X-Content-Type-Options","nosniff");let s=t.send.bind(t);t.send=(i=>{let d=String(t.getHeader("Content-Type")??"").toLowerCase(),l=d.startsWith("application/json"),c=d.startsWith("text/markdown");if(r==="native"||!l&&!c||t.headersSent)return s(i);let u=typeof i=="string"?i:Buffer.isBuffer(i)?i.toString("utf8"):void 0;return u===void 0?s(i):r==="plain"?(t.setHeader("Content-Type",vh),s(u)):(t.setHeader("Content-Type",kh),t.setHeader("Content-Security-Policy",Ah),s(Ph(l?Ch(u):u,o)))}),n()}}a(rl,"getLinkContentNegotiation");Jr();y();Ye();function Eh(e){let t=_e(e.settings.httpAdminRoot),n=Rt(e.settings.httpAdminRoot),r="0.2.0-beta.9",o=[{method:"GET",path:"/",auth:"none",summary:"This discovery index"},{method:"GET",path:"/openapi.yaml",auth:"none",summary:"OpenAPI 3.1 specification (YAML)"},{method:"GET",path:"/openapi.json",auth:"none",summary:"OpenAPI 3.1 specification (JSON)"},{method:"GET",path:"/status",auth:"admin-or-access-link",summary:"Plugin status and caller info"},{method:"GET",path:"/flows",auth:"admin-or-access-link",summary:"List flows + rev"},{method:"GET",path:"/flows/{id}",auth:"admin-or-access-link",summary:"Get one flow tab + rev (?summary=1 for node list without field contents)"},{method:"GET",path:"/nodes/{id}",auth:"admin-or-access-link",summary:"Get one full node (optional ?flow=)"},{method:"GET",path:"/nodes/{id}/field/{name}",auth:"admin-or-access-link",summary:"Get one node field (?lines=A-B optional)"},{method:"GET",path:"/search",auth:"admin-or-access-link",summary:"Text search across string fields (?q=&flow=)"},{method:"GET",path:"/node-types",auth:"admin-or-access-link",summary:"List installed palette node types (?q= filter)"},{method:"GET",path:"/node-types/{type}",auth:"admin-or-access-link",summary:"Detail for one installed node type (defaults JSON, inputs/outputs, help; ?help=0 omits help)"},{method:"GET",path:"/palette/search",auth:"admin-or-access-link",summary:"Search Node-RED catalogue (recommend only; never installs) (?q=)"},{method:"POST",path:"/proposals",auth:"access-link",summary:"Create a replace_flow proposal"},{method:"GET",path:"/proposals",auth:"admin-or-access-link",summary:"List proposals (admin: all; link: own)"},{method:"GET",path:"/proposals/{id}",auth:"admin-or-access-link",summary:"Get one proposal"},{method:"GET",path:"/proposals/{id}/diff",auth:"admin-or-access-link",summary:"Diff proposal vs current flow"}];return{ok:!0,name:"node-red-ai-access",version:r,description:"Temporary, owner-approved Node-RED access for AI clients over plain HTTP (REST) or MCP.",auth:{scheme:"Bearer",header:"Authorization: Bearer <token>",description:"An admin with * mints tokens on the /ai-access dashboard (Access tokens tab) (POST /links only \u2014 never under GET /mcp/a/<token>). Each token has kind get_link | rest | mcp and only works on that surface. Send rest/mcp tokens as Authorization: Bearer <token>. Missing/invalid \u2192 401 access_link_missing / access_link_invalid; wrong kind \u2192 403 wrong_token_kind. GET /mcp/a links cannot create tokens.",tokenKinds:["get_link","rest","mcp"],mint:{path:"/links",method:"POST",auth:"admin",note:"Admin * only. Not available under /mcp/a/<token>."}},openapi:`${t}/openapi.yaml`,openapiJson:`${t}/openapi.json`,mcp:n,apiBase:t,getLink:{base:"/mcp/a/<token>",canMintTokens:!1,note:"Self-contained GET surface for get_link tokens only. No POST /links equivalent."},endpoints:o}}a(Eh,"buildDiscoveryIndex");function Ni(e){return(t,n)=>{n.status(200).json(Eh(e))}}a(Ni,"discoveryIndexHandler");function ol(){return(e,t)=>{t.status(404).json({error:"Not found",code:"not_found"})}}a(ol,"mcpApiNotFoundHandler");y();y();var Bn;function qn(){return Bn||(Bn="0.2.0-beta.9",Bn)}a(qn,"packageVersion");var Wn=qn();function Ih(){return`#red-ui-sidebar-ai-access {
|
|
13
13
|
position: relative;
|
|
14
14
|
height: 100%;
|
|
15
15
|
display: flex;
|
|
@@ -165,7 +165,7 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
|
|
|
165
165
|
border: 0;
|
|
166
166
|
background: transparent;
|
|
167
167
|
color: transparent;
|
|
168
|
-
}`}a(
|
|
168
|
+
}`}a(Ih,"editorSidebarStyles");function Lh(){return`
|
|
169
169
|
/**
|
|
170
170
|
* Editor right-hand sidebar tab: AI Access.
|
|
171
171
|
* Served from /mcp/editor/ai-access-sidebar.js (no-store + ?v= cache-bust).
|
|
@@ -463,7 +463,7 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
|
|
|
463
463
|
saving = false;
|
|
464
464
|
});
|
|
465
465
|
}
|
|
466
|
-
`}a(
|
|
466
|
+
`}a(Lh,"editorSidebarScriptPartA");function Nh(){return`
|
|
467
467
|
function buildContent() {
|
|
468
468
|
var content = $('<div></div>', { id: CONTENT_ID, class: 'red-ui-sidebar-ai-access' });
|
|
469
469
|
|
|
@@ -565,7 +565,7 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
|
|
|
565
565
|
},
|
|
566
566
|
});
|
|
567
567
|
})();
|
|
568
|
-
`}a(
|
|
568
|
+
`}a(Nh,"editorSidebarScriptPartB");function Fh(){return Lh()+Nh()}a(Fh,"editorSidebarScriptBody");function Oh(){return`(function () {
|
|
569
569
|
if (typeof RED === 'undefined' || typeof BroadcastChannel === 'undefined') return;
|
|
570
570
|
if (window.__com55AiAccessRevealListener) return;
|
|
571
571
|
window.__com55AiAccessRevealListener = true;
|
|
@@ -596,8 +596,8 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
|
|
|
596
596
|
try { window.focus(); } catch (e) {}
|
|
597
597
|
}
|
|
598
598
|
};
|
|
599
|
-
})();`}a(
|
|
600
|
-
`)}a(
|
|
599
|
+
})();`}a(Oh,"editorRevealListenerScript");function qo(){let e=JSON.stringify(Ih());return[`/* @com55/node-red-ai-access editor sidebar v=${Wn} */`,"(function(){"," if (typeof document === 'undefined') return;"," if (!document.getElementById('com55-ai-access-sidebar-css')) {"," var style = document.createElement('style');"," style.id = 'com55-ai-access-sidebar-css';",` style.textContent = ${e};`," document.head.appendChild(style);"," }","})();",Oh(),Fh()].join(`
|
|
600
|
+
`)}a(qo,"renderEditorSidebarJs");function sl(e=Wn){return`<script>
|
|
601
601
|
/**
|
|
602
602
|
* Thin loader for the AI Access editor sidebar.
|
|
603
603
|
* Logic lives at GET /mcp/editor/ai-access-sidebar.js (Cache-Control: no-store)
|
|
@@ -631,8 +631,8 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
|
|
|
631
631
|
(document.head || document.documentElement).appendChild(s);
|
|
632
632
|
})();
|
|
633
633
|
</script>
|
|
634
|
-
`}a(
|
|
635
|
-
\u2026 (${o} bytes total; use ?format=full for complete text) \u2026`},"trimText");return{...e,keyFields:e.keyFields.map(r=>r.kind==="text"?{...r,value:n(r.value)}:r),properties:e.properties.map(r=>r.kind==="text"?{...r,value:n(r.value)}:r)}}a(
|
|
634
|
+
`}a(sl,"renderEditorSidebarLoaderHtml");y();le();ct();Ne();function il(e){return ut[e]??"Request failed"}a(il,"safeErrorText");function $h(e,t){let n=J(t,"rest");if(!n)return!1;let r=n.body.code,o={error:il(r),code:r};return typeof n.body.hint=="string"&&(o.hint=n.body.hint),typeof n.body.expectedRev=="string"&&(o.expectedRev=n.body.expectedRev),typeof n.body.currentRev=="string"&&(o.currentRev=n.body.currentRev),e.setHeader("X-Content-Type-Options","nosniff"),e.status(n.statusCode).json(o),!0}a($h,"sendMappedFlowError");function al(e){return async(t,n)=>{try{n.json(await vt(rt(t,e)))}catch(r){if($h(n,r))return;n.setHeader("X-Content-Type-Options","nosniff"),n.status(502).json({error:il("upstream_error"),code:"upstream_error"})}}}a(al,"listFlowsHandler");y();Re();le();Ye();ct();y();function zn(e){return e==null||e===""?{ok:!0,format:"summary"}:e==="summary"||e==="full"?{ok:!0,format:e}:{ok:!1}}a(zn,"resolveDiffFormat");function Mh(e,t=2){if(e.length===0)return{lines:e,truncated:!1};let n=new Array(e.length).fill(!1);for(let i=0;i<e.length;i+=1){let d=e[i];if(!d||d.op==="equal")continue;let l=Math.max(0,i-t),c=Math.min(e.length-1,i+t);for(let u=l;u<=c;u+=1)n[u]=!0}if(!n.some(Boolean)){let i=Math.min(e.length,t*2+1);for(let d=0;d<i;d+=1)n[d]=!0}let r=[],o=!1,s=0;for(;s<e.length;){if(n[s]){let l=e[s];l&&r.push(l),s+=1;continue}let i=s;for(;i<e.length&&!n[i];)i+=1;let d=i-s;o=!0,r.push({op:"equal",text:`\u2026 (${d} unchanged line${d===1?"":"s"}) \u2026`}),s=i}return{lines:r,truncated:o}}a(Mh,"compactLineDiff");function Dh(e,t){let n=e.before??"",r=e.after??"",o=Buffer.byteLength(n,"utf8"),s=Buffer.byteLength(r,"utf8");if(t==="full")return{...e,before:n,after:r,beforeBytes:o,afterBytes:s};let i=Mh(e.lines),d={key:e.key,kind:"text",beforeBytes:o,afterBytes:s,lines:i.lines};return i.truncated&&(d.truncated=!0),d}a(Dh,"formatTextChange");function Hh(e,t){return e.map(n=>n.kind==="text"?Dh(n,t):n)}a(Hh,"formatPropertyChanges");function dl(e,t){if(t==="full")return e;let n=a(r=>{if(typeof r!="string")return r;let o=Buffer.byteLength(r,"utf8");return o<=4e3?r:`${r.slice(0,500)}
|
|
635
|
+
\u2026 (${o} bytes total; use ?format=full for complete text) \u2026`},"trimText");return{...e,keyFields:e.keyFields.map(r=>r.kind==="text"?{...r,value:n(r.value)}:r),properties:e.properties.map(r=>r.kind==="text"?{...r,value:n(r.value)}:r)}}a(dl,"formatPresenceText");function Fi(e,t){return{summary:e.summary,...e.tabProperties?{tabProperties:e.tabProperties}:{},added:e.added.map(n=>dl(n,t)),removed:e.removed.map(n=>dl(n,t)),modified:e.modified.map(n=>({...n,changes:Hh(n.changes,t)}))}}a(Fi,"formatSections");function Gn(e,t="summary"){let n=Fi(e,t),r={...e,...n,format:t};return e.liveChanges&&(r.liveChanges=Fi(e.liveChanges,t)),e.vsCurrent&&(r.vsCurrent=Fi(e.vsCurrent,t)),r}a(Gn,"applyDiffFormat");y();de();Re();Do();function Kn(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}a(Kn,"escapeForDocs");var ll="https://YOUR-NODE-RED/mcp/rpc";function jh(e,t,n,r=ll){return`## Also available (prefer when your client can send headers)
|
|
636
636
|
|
|
637
637
|
**Prefer MCP or REST** whenever the AI client can send custom headers and JSON
|
|
638
638
|
bodies \u2014 no URL length limits and no chunked drafts. Use this **GET link
|
|
@@ -701,7 +701,7 @@ curl -sS -X POST "${r}" \\
|
|
|
701
701
|
-H "Accept: application/json, text/event-stream" \\
|
|
702
702
|
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
|
|
703
703
|
\`\`\`
|
|
704
|
-
`}a(
|
|
704
|
+
`}a(jh,"docsAlsoAvailable");function Uh(e,t){return`# Node-RED AI Access \u2014 self-contained GET link (get_link)${e}
|
|
705
705
|
|
|
706
706
|
**Git mental model:** a proposal is like a pull request. Human approve = merge;
|
|
707
707
|
reject = close; withdraw = you close your own PR. Revision changes auto-rebase
|
|
@@ -779,7 +779,7 @@ fields at the top level** (no \`ok\` / \`proposal\` wrapper). Read
|
|
|
779
779
|
|
|
780
780
|
Lean by default (no \`payload\` / \`baseFlow\`). Pass
|
|
781
781
|
\`?include=payload,baseFlow\` or \`?include_payload=true\` to include them.
|
|
782
|
-
`}a(
|
|
782
|
+
`}a(Uh,"docsIntro");function Bh(){return"## Step-by-step workflow\n\n1. `GET ./status` \u2014 confirm the link works; note `caller.expiresAt` /\n `caller.minutesRemaining`.\n2. `GET ./flows` \u2014 list tabs + current `rev`.\n3. **Layered read (prefer this before loading whole tabs):**\n - `GET ./flows/<id>?summary=1` \u2014 node list with `id`/`type`/`name`/`wires` +\n per-node `bytes` (no field contents).\n - `GET ./search?q=<text>&flow=<id>` \u2014 hits with node id, field, line numbers,\n short snippet (`truncated:true` when the result cap is hit).\n - `GET ./nodes/<id>` \u2014 one full node (`flowId` included when known).\n - `GET ./nodes/<id>/field/<name>?lines=A-B` \u2014 one field (optional 1-based\n line range) plus `totalLines` and `bytes`.\n4. Prefer a **targeted text replace** for small edits inside long string fields\n (`template`, `func`, \u2026) \u2014 unique `find` (or `occurrence`) keeps the diff short:\n\n```\nGET ./propose?flow=<flowId>&rev=<rev>&node=<nodeId>&field=<prop>&find=<text>&replace=<text>&occurrence=<n>&title=<text>\n```\n\n5. Or set whole properties (optional `title` \u226480 chars; `summary` is a title fallback):\n\n```\nGET ./propose?flow=<flowId>&rev=<rev>&node=<nodeId>&set.<prop>=<urlencoded>&title=<text>\n```\n\n6. Or multi-node / add / remove / `replace_text` / full tab via base64url JSON patch:\n\n```\nGET ./propose?flow=<flowId>&rev=<rev>&patch=<base64url-json>&title=<text>\n```\n\n7. Poll `GET ./proposals` / `./proposals/<id>` until approved, rejected, or\n withdrawn. Every token can list/read **all** proposals; each item has\n `mine:true|false`. Filter with `?mine=true` (own) or `?mine=false` (others'),\n `?status=pending,superseded` (comma-separated or repeated), `?flowId=`;\n paginate with `?limit=` (1\u2013200) / `?offset=` (response includes `total`,\n `offset`, `hasMore`, and `nextOffset` when more remain). Invalid filters\n return `400 invalid_filter` naming the parameter. Cite `displayName` +\n `reviewUrl` when updating the user.\n8. Optional: `GET ./proposals/<id>/diff` (default `format=summary` compact\n line hunks + byte sizes; `format=full` for complete text) against the\n **base snapshot** stored at submit. Added/removed nodes are presence blocks\n with a summary sentence; open proposals include `rebase` and `liveChanges`; `vsCurrent` shows the merged deployment when there are no conflicts.\n9. Withdraw your own open (pending or conflict) proposal:\n `GET ./proposals/<id>/withdraw?confirm=1&reason=<text>` (status becomes `withdrawn`,\n not `rejected`). Only the link that created a proposal can withdraw it;\n others get `403 proposal_not_owned`. If the token is lost, an admin with full permission can withdraw using an authenticated dashboard session via `POST /mcp/api/v1/proposals/<id>/withdraw`; this recovery is audit-logged.\n\n**Recommended edit loop:** summary \u2192 search \u2192 read a line range \u2192\n`replace_text` \u2192 propose.\n\nOn submit and on read of an open (pending or conflict) proposal, the response may include\n`conflictingProposals` (overlapping open work; list omits this) with\n`overlappingNodeIds` / `overlappingFields` when detectable \u2014 treat like a\ngit conflict warning; prefer one pending change per flow when possible.\n\n## Node types and palette (read-only)\n\nBefore proposing unfamiliar nodes:\n\n1. `GET ./node-types?q=<text>` \u2014 list installed palette types (filter optional;\n results capped; may include `truncated` / `total`).\n2. `GET ./node-types/<type>` \u2014 detail (`defaults` as JSON, `inputs`/`outputs`,\n `requiredConfigNodeTypes`, help sections). Pass `?help=0` to omit help.\n Types are **case-sensitive** (`Debug` \u2192 did you mean `debug`?).\n3. `GET ./palette/search?q=<text>` \u2014 search the public Node-RED catalogue.\n **Recommend only** \u2014 ask the user to install via **Manage palette**, then\n recheck `./node-types`. This API **never installs** packages.\n Core `node-red` types are **not** in the catalogue \u2014 use `./node-types`.\n\nSubmit/propose with an unknown `node.type` returns `400 unknown_node_type`\nwith `nearest` suggestions \u2014 call node-types / palette search and ask the user\nto install the module, then retry.\n"}a(Bh,"docsWorkflow");function qh(){return'## Exact URLs for this token (relative)\n\n| Purpose | Method + path |\n| --- | --- |\n| These docs | `GET ./` (this URL) |\n| Status | `GET ./status` |\n| List flows | `GET ./flows` |\n| One flow | `GET ./flows/<id>` |\n| Flow summary (no field bodies) | `GET ./flows/<id>?summary=1` |\n| One node | `GET ./nodes/<id>` |\n| One field (optional lines) | `GET ./nodes/<id>/field/<name>?lines=A-B` |\n| Text search | `GET ./search?q=&flow=` |\n| List node types | `GET ./node-types?q=` |\n| One node type | `GET ./node-types/<type>` (`?help=0` omits help) |\n| Palette search (recommend only) | `GET ./palette/search?q=` |\n| All proposals (`?mine=` / `?status=` / `?flowId=` / `?limit=` / `?offset=`) | `GET ./proposals` |\n| One proposal | `GET ./proposals/<id>` |\n| Proposal diff | `GET ./proposals/<id>/diff` |\n| Withdraw own pending | `GET ./proposals/<id>/withdraw?confirm=1&reason=` |\n| Propose (query) | `GET ./propose?\u2026` |\n| Draft start | `GET ./draft/new?flow=&rev=&node=&field=&title=` |\n| Draft chunk | `GET ./draft/<draftId>/append?seq=N&data=<base64url>` |\n| Draft submit | `GET ./draft/<draftId>/submit?sha256=<hex>&title=` |\n\nThere is **no** `POST ./links` (or any create-link) under this URL \u2014 GET links\n**cannot mint tokens**.\n\nUnknown paths under this link return JSON `{ "code": "not_found" }` (never HTML,\nand the token is never echoed). Attempted mint paths (`./links`) return\n`403 link_create_not_supported`.\n'}a(qh,"docsUrlTable");function Wh(){return`## Propose examples
|
|
783
783
|
|
|
784
784
|
**Recommended for small edits to long fields** \u2014 \`replace_text\` (find must be
|
|
785
785
|
**unique** unless you pass \`occurrence\` as a 1-based index). Creates a normal
|
|
@@ -857,11 +857,11 @@ GET ./propose?flow=tab1&rev=abc123&patch=<base64url>
|
|
|
857
857
|
Node-level proposes create a normal \`replace_flow\` proposal; owners should
|
|
858
858
|
approve with deployment type **\`nodes\`** (plugin default) so only changed
|
|
859
859
|
nodes deploy.
|
|
860
|
-
`}a(
|
|
860
|
+
`}a(Wh,"docsProposeExamples");function zh(){return`## Chunked drafts (long HTML / large fields)
|
|
861
861
|
|
|
862
|
-
When the GET URL would exceed **${
|
|
862
|
+
When the GET URL would exceed **${Tn} bytes**, do not
|
|
863
863
|
stuff everything into \`propose\`. Use drafts (expire after **30 minutes**,
|
|
864
|
-
max **${
|
|
864
|
+
max **${Y}** decoded bytes):
|
|
865
865
|
|
|
866
866
|
### Recommended chunk size
|
|
867
867
|
|
|
@@ -906,7 +906,7 @@ Or a full \`replace_flow\` document. JSON-mode drafts **may add nodes**, so
|
|
|
906
906
|
\`draft/new\` does not require an existing \`node\`/\`field\`. Use JSON mode for
|
|
907
907
|
structural changes; use field-mode only when you need to replace one whole
|
|
908
908
|
string field that is too large for a single propose URL.
|
|
909
|
-
`}a(
|
|
909
|
+
`}a(zh,"docsDrafts");function Gh(){return`## Last resort: content larger than chunked drafts
|
|
910
910
|
|
|
911
911
|
If the field is still too large for chunked drafts (decoded size over the
|
|
912
912
|
proposal cap), use this fallback only after drafts fail:
|
|
@@ -920,9 +920,9 @@ proposal cap), use this fallback only after drafts fail:
|
|
|
920
920
|
**Trade-off:** pasted content is **not** reviewed through the proposal diff.
|
|
921
921
|
Prefer \`replace_text\` or chunked drafts whenever they fit so the human can
|
|
922
922
|
still see a readable change on \`/mcp\`.
|
|
923
|
-
`}a(
|
|
923
|
+
`}a(Gh,"docsLastResortPaste");function Kh(){return`## Size cap and REST/MCP nodes payload shapes
|
|
924
924
|
|
|
925
|
-
The proposal JSON cap is **${
|
|
925
|
+
The proposal JSON cap is **${Y} bytes**
|
|
926
926
|
(\`MAX_PROPOSAL_JSON_BYTES\` / ~1.5MB) on **every** channel: GET propose/drafts,
|
|
927
927
|
REST \`POST /proposals\`, and MCP \`submit_proposal\`. Oversized bodies return
|
|
928
928
|
\`413 payload_too_large\` (never truncated).
|
|
@@ -966,7 +966,7 @@ Example (canonical):
|
|
|
966
966
|
**Alternative (also accepted):** top-level \`nodes\` array as an alias for
|
|
967
967
|
\`payload.nodes\` (same op shapes). Full-tab replace remains supported via
|
|
968
968
|
\`payload.flow\` (from \`GET ./flows/<id>\` / REST \`GET /flows/{id}\`).
|
|
969
|
-
`}a(
|
|
969
|
+
`}a(Kh,"docsRestNodesPayload");function Jh(){return`## Known issues
|
|
970
970
|
|
|
971
971
|
- **Rate limits are per token (and per IP).** Each access-link token has its own
|
|
972
972
|
token bucket. Several tokens can each use the full per-token limit at the same
|
|
@@ -974,20 +974,20 @@ Example (canonical):
|
|
|
974
974
|
- **\`RateLimit-*\` headers describe the per-token bucket** (default
|
|
975
975
|
\`RateLimit-Policy: 300;w=60\`) on token requests; requests without a token
|
|
976
976
|
report the per-IP bucket (\`600;w=60\`). Both are enforced.
|
|
977
|
-
`}a(
|
|
977
|
+
`}a(Jh,"docsKnownIssues");function Vh(){return`## Error codes (JSON unless docs)
|
|
978
978
|
|
|
979
|
-
${
|
|
979
|
+
${Xd()}
|
|
980
980
|
|
|
981
981
|
All JSON responses use \`Cache-Control: no-store\`. Never put secrets in query
|
|
982
982
|
strings you will paste publicly beyond this temporary link.
|
|
983
|
-
`}a(
|
|
984
|
-
`)}a(Gd,"renderGetLinkDocs");var rl=require("crypto");se();ie();tt();zt();Fe();et();ie();function No(e){return{error:e instanceof Error&&e.message.trim()?e.message:"Internal server error",code:"internal_error"}}a(No,"internalErrorBody");function Ch(e,t){e.headersSent||e.status(500).json(No(t))}a(Ch,"sendInternalError");function B(e,t){if(e.headersSent)return;let n=W(t);if(n){e.status(n.statusCode).json(n.body);return}Ch(e,t)}a(B,"sendCaughtRouteError");function O(e){return(t,n,r)=>{Promise.resolve().then(()=>e(t,n,r)).catch(o=>{if(n.headersSent){r(o instanceof Error?o:new Error(String(o)));return}B(n,o)})}}a(O,"asyncRoute");var Oo=require("crypto");se();ve();var G=class extends Error{static{a(this,"DraftValidationError")}statusCode=400;code;constructor(t,n="draft_invalid"){super(t),this.name="DraftValidationError",this.code=n}},Qt=class extends Error{static{a(this,"DraftNotFoundError")}statusCode=404;code="draft_not_found";constructor(t){super(`Draft '${t}' was not found or has expired`),this.name="DraftNotFoundError"}},en=class extends Error{static{a(this,"DraftAlreadySubmittedError")}statusCode=409;code="draft_already_submitted";proposalId;constructor(t,n){super(`Draft '${t}' was already submitted as proposal '${n}'`),this.name="DraftAlreadySubmittedError",this.proposalId=n}},Ih=/^[A-Za-z0-9_-]+={0,2}$/;function Kd(e){if(!Ih.test(e)||e.replace(/=+$/,"").length===0)throw new G("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data");let t;try{t=Buffer.from(e,"base64url")}catch{throw new G("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data")}if(t.byteLength===0&&e.length>0)throw new G("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data");return t}a(Kd,"assertValidBase64UrlChunk");var Fo=class{static{a(this,"DraftStore")}drafts=new Map;submitted=new Map;now;randomId;maxBytes;ttlMs;constructor(t={}){this.now=t.now??(()=>Date.now()),this.randomId=t.randomId??(()=>(0,Oo.randomBytes)(12).toString("hex")),this.maxBytes=t.maxBytes??K,this.ttlMs=t.ttlMs??ca}purgeExpired(t){for(let[n,r]of this.drafts)r.expiresAtMs<=t&&this.drafts.delete(n);for(let[n,r]of this.submitted)r.expiresAtMs<=t&&this.submitted.delete(n)}create(t){let n=this.now();this.purgeExpired(n);let r=this.randomId(),o={id:r,linkId:t.linkId,flowId:t.flowId,expectedRev:t.expectedRev,mode:t.mode,chunks:new Map,createdAtMs:n,expiresAtMs:n+this.ttlMs,totalBytes:0};return t.nodeId!==void 0&&(o.nodeId=t.nodeId),t.field!==void 0&&(o.field=t.field),t.targets!==void 0&&(o.targets=t.targets),t.summary!==void 0&&(o.summary=t.summary),t.title!==void 0&&(o.title=t.title),this.drafts.set(r,o),o}getForLink(t,n){let r=this.now();this.purgeExpired(r);let o=this.submitted.get(t);if(o?.linkId===n)throw new en(t,o.proposalId);let s=this.drafts.get(t);if(s?.linkId!==n)throw new Qt(t);if(s.expiresAtMs<=r)throw new Qt(t);return s}markSubmitted(t,n,r){let o=this.now();this.drafts.delete(t),this.submitted.set(t,{linkId:n,proposalId:r,expiresAtMs:o+this.ttlMs})}append(t,n,r,o){if(!Number.isInteger(r)||r<0)throw new G("seq must be a non-negative integer","invalid_seq");if(typeof o!="string")throw new G("data must be a base64url string","invalid_chunk");let s=this.getForLink(t,n),i=s.chunks.get(r);if(i!==void 0){if(i!==o)throw new G(`Chunk seq ${r} already stored with different data`,"chunk_conflict")}else{let u=Kd(o),p=s.totalBytes+u.byteLength;if(p>this.maxBytes)throw new G(`Draft exceeds limit of ${this.maxBytes} bytes (decoded)`,"payload_too_large");s.chunks.set(r,o),s.totalBytes=p}let d=[...s.chunks.keys()].sort((u,p)=>u-p),l=[],c=d.at(-1);if(c!==void 0)for(let u=0;u<=c;u+=1)s.chunks.has(u)||l.push(u);return{draft:s,receivedSeqs:d,missingSeqs:l,totalBytes:s.totalBytes,maxBytes:this.maxBytes}}getMaxBytes(){return this.maxBytes}assembleAndVerify(t,n,r){let o=this.getForLink(t,n);if(o.chunks.size===0)throw new G("Draft has no chunks","draft_empty");let i=[...o.chunks.keys()].sort((h,m)=>h-m).at(-1);if(i===void 0)throw new G("Draft has no chunks","draft_empty");let d=[];for(let h=0;h<=i;h+=1)o.chunks.has(h)||d.push(h);if(d.length>0)throw new G(`Missing chunk seqs: ${d.join(",")}`,"missing_chunks");let l=[];for(let h=0;h<=i;h+=1){let m=o.chunks.get(h);if(m===void 0)throw new G(`Missing chunk seqs: ${h}`,"missing_chunks");l.push(m)}let c=l.map(h=>Kd(h)),u=Buffer.concat(c);if(u.byteLength>this.maxBytes)throw new G(`Assembled draft exceeds limit of ${this.maxBytes} bytes`,"payload_too_large");let p=r.trim().toLowerCase();if(!/^[0-9a-f]{64}$/.test(p))throw new G("invalid_sha256: expected 64 hex chars","invalid_sha256");if((0,Oo.createHash)("sha256").update(u).digest("hex")!==p)throw new G("sha256 does not match assembled draft content","draft_hash_mismatch");return{draft:o,assembled:u.toString("utf8")}}size(){return this.purgeExpired(this.now()),this.drafts.size}clear(){this.drafts.clear(),this.submitted.clear()}};Ao();$t();ve();var Et=zn(require("express-rate-limit"),1);var wi=6e4,Jd=600,Vd=300;function yi(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?Math.max(1,Math.floor(e)):t}a(yi,"positiveInt");function pt(e){if(!e)return{windowMs:wi,maxPerIp:Jd,maxPerToken:Vd};let t=e;return{windowMs:yi(t.windowMs??t.rateLimitWindowMs,wi),maxPerIp:yi(t.maxPerIp??t.rateLimitMaxPerIp,Jd),maxPerToken:yi(t.maxPerToken??t.rateLimitMaxPerToken,Vd)}}a(pt,"resolveRateLimitConfig");function Lh(e,t){let r=e.rateLimit?.resetTime;return r instanceof Date&&!Number.isNaN(r.getTime())?Math.max(1,Math.ceil((r.getTime()-Date.now())/1e3)):Math.max(1,Math.ceil(t/1e3))}a(Lh,"retryAfterSeconds");function Yd(e,t,n=wi){let r=Lh(e,n);t.setHeader("Retry-After",String(r)),t.setHeader("Cache-Control","no-store"),t.status(429).json({error:"Too many requests. Slow down and retry.",code:"rate_limited",retryAfter:r,hint:"Wait retryAfter seconds, then retry. Prefer summary/search reads over full field downloads."})}a(Yd,"sendRateLimited");function Nh(e){return(t,n,r,o)=>{Yd(t,n,e)}}a(Nh,"rateLimitedHandler");function $o(e){return`ip:${(0,Et.ipKeyGenerator)(e.ip??"unknown")}`}a($o,"clientIpKey");function Mo(e){let t=e.params.token;if(typeof t=="string"&&t.length>0)return`token:${t.slice(0,48)}`;let n=e.get("authorization")??e.headers.authorization;if(typeof n=="string"){let s=/^Bearer\s+(\S+)/i.exec(n.trim())?.[1];if(s&&(s.startsWith("aia_")||s.length>=24))return`token:${s.slice(0,48)}`}let r=e;return r.accessLink?.id?`token:link:${r.accessLink.id}`:null}a(Mo,"accessTokenRateLimitKey");function Xd(e,t,n,r){let o={windowMs:t,max:e,standardHeaders:!0,legacyHeaders:!1,keyGenerator:n,handler:Nh(t)};return r&&(o.skip=r),(0,Et.default)(o)}a(Xd,"createLimiter");function bi(e){let t=pt(e);return Xd(t.maxPerIp,t.windowMs,$o)}a(bi,"createAiAccessRateLimiter");function Fh(e){let t=pt(e);return Xd(t.maxPerToken,t.windowMs,n=>Mo(n)??$o(n),n=>Mo(n)==null)}a(Fh,"createAiAccessTokenRateLimiter");function Zd(e){let t=pt(e),n=bi(t),r=Fh(t);return{ip:n,token:r,stack:[n,r],config:t}}a(Zd,"createAiAccessDualRateLimiters");function Qd(e){let t=pt(e()).windowMs,n=pt(e()).maxPerIp,r=pt(e()).maxPerToken,o=new Et.MemoryStore,s=new Et.MemoryStore,i=a(()=>{let m=pt(e());if(m.windowMs!==t||m.maxPerIp!==n||m.maxPerToken!==r){let g=m.windowMs!==t;t=m.windowMs,n=m.maxPerIp,r=m.maxPerToken,o.resetAll(),s.resetAll(),g&&(o.init?.({windowMs:t}),s.init?.({windowMs:t}))}return m},"refreshWindow"),d=new WeakSet,l=a(m=>{if(d.has(m))return;d.add(m);let g=m.setHeader.bind(m);m.setHeader=((y,A)=>(y.toLowerCase()==="ratelimit-policy"&&typeof A=="string"&&(A=A.replace(/;w=\d+/,`;w=${Math.ceil(t/1e3)}`)),g(y,A)))},"useLiveWindowPolicy"),c=a((m,g)=>(0,Et.default)({store:g,windowMs:t,limit:a(()=>{let y=pt(e());return m?y.maxPerToken:y.maxPerIp},"limit"),standardHeaders:!0,legacyHeaders:!1,keyGenerator:m?y=>Mo(y)??$o(y):$o,skip:a(y=>m&&Mo(y)==null,"skip"),handler:a((y,A,x)=>{Yd(y,A,pt(e()).windowMs)},"handler")}),"make"),u=c(!1,o),p=c(!0,s);return{ip:a((m,g,y)=>(i(),l(g),u(m,g,y)),"ip"),token:a((m,g,y)=>(i(),l(g),p(m,g,y)),"token")}}a(Qd,"createLiveAiAccessDualRateLimiters");ke();function z(e){e.setHeader("Cache-Control","no-store"),e.setHeader("Pragma","no-cache")}a(z,"setNoStore");function vi(e){let t=e.originalUrl||e.url||"",n=ks(t);n!==t&&(Object.defineProperty(e,"originalUrl",{value:n,writable:!0,configurable:!0}),typeof e.url=="string"&&Object.defineProperty(e,"url",{value:ks(e.url),writable:!0,configurable:!0}))}a(vi,"redactTokenFromRequestUrl");function el(e,t){return(n,r,o)=>{z(r);let s=n.originalUrl||n.url||"";if(n.getLinkUrlBytes=Buffer.byteLength(s,"utf8"),vi(n),!Ce(e.settings)){r.status(403).json({error:"AI Access features are disabled. Enable adminAuth, or set aiAccess.allowWithoutAdminAuth to true for local-only use.",code:"ai_features_disabled",hint:"Ask the Node-RED owner to enable AI Access."});return}let i=n.params.token;if(typeof i!="string"||i.length===0){r.setHeader("WWW-Authenticate",ce),r.status(401).json({error:xe,code:qe,hint:"Ask the user for a new self-contained access link URL."});return}let d=i;try{d=decodeURIComponent(i)}catch{}let l=t.validateResult(d);if(l.status==="expired"){tr(n,r,e.settings.httpAdminRoot,l.expiresAt);return}if(l.status!=="active"){let c=d.length<24||d.startsWith("aia_")&&d.length<40?"The access-link URL may be truncated \u2014 ask the user for the full /mcp/a/<token> link.":"Ask the user for a new self-contained access link URL (it may have expired, or Node-RED may have restarted).";r.setHeader("WWW-Authenticate",ce),r.status(401).json({error:Ot,code:er,hint:c});return}if(l.link.kind!=="get_link"){nr(r,"get_link",l.link.kind);return}n.accessLink=l.link,n.getLinkToken=d,o()}}a(el,"requireGetLinkToken");function Fn(e,t){let n=e.getLinkUrlBytes;return(typeof n=="number"?n:Buffer.byteLength(e.originalUrl||e.url||"","utf8"))>gn?(z(t),t.status(414).json({error:`Request URL exceeds ${gn} bytes`,code:"url_too_long",hint:"Use chunked drafts: GET \u2026/draft/new then \u2026/draft/<id>/append?seq=&data= then \u2026/draft/<id>/submit?sha256=, or use POST /mcp/api/v1/proposals with Authorization: Bearer <token>.",limitBytes:gn}),!0):!1}a(Fn,"rejectOversizedGetUrl");function j(e,t,n,r,o,s){z(e),e.status(t).json({error:n,code:r,...o?{hint:o}:{},...s})}a(j,"jsonError");function tl(){return(e,t)=>{vi(e),z(t),t.status(404).json({error:"Not found",code:"not_found"})}}a(tl,"getLinkNotFoundHandler");var Oh="link_create_not_supported";function nl(){return(e,t)=>{vi(e),z(t),t.status(403).json({error:"GET access links cannot create tokens",code:Oh,hint:"Ask an admin with * permission to mint tokens via POST /mcp/api/v1/links on the /ai-access dashboard (Access tokens tab). This get_link URL cannot create, list, or revoke tokens."})}}a(nl,"getLinkMintNotSupportedHandler");function M(e,t){let n=e[t];if(typeof n=="string"&&n.length>0)return n;if(Array.isArray(n)&&typeof n[0]=="string"&&n[0].length>0)return n[0]}a(M,"queryString");function He(e){switch(e){case"draft_not_found":return"Start a new draft with GET \u2026/draft/new.";case"draft_already_submitted":return"This draft already created a proposal; poll that proposal or start a new draft.";case"chunk_conflict":return"Re-send the same data for that seq, or start a new draft.";case"payload_too_large":return"Shrink the content; totalBytes counts decoded bytes against maxBytes.";case"invalid_seq":return"seq must be a non-negative integer (0, 1, 2, \u2026).";case"invalid_chunk":case"invalid_chunk_data":return"data must be base64url of the raw chunk bytes (A-Z, a-z, 0-9, -, _).";case"missing_chunks":return"Upload every seq from 0 through max before submit.";case"invalid_sha256":return"sha256 must be exactly 64 hexadecimal characters.";case"draft_hash_mismatch":return"sha256 must be the hex digest of the assembled UTF-8 bytes (before base64url).";case"draft_empty":return"Append at least one chunk before submit.";case"node_not_found":return"Call GET \u2026/flows/<id> and use a node id from that response (or use mode=json with op:add).";case"field_not_found":return"Use a property that exists on the node, or mode=json to add/replace nodes.";case"missing_draft_params":return"Example: \u2026/draft/new?flow=tab1&rev=abc&node=n1&field=template";case"version_mismatch":return"Re-read the flow and start a new draft with the current rev.";case"protected_property":return"Omit id, type, and z from set props. wires are allowed when targets exist in the flow.";case"invalid_wires":return"wires must list existing node ids (or add those nodes in the same patch).";default:return}}a(He,"draftErrorHint");async function $h(e,t,n){let r;try{r=await e.getFlow(t)}catch(s){let i=W(s);if(i)return{code:i.body.code,message:i.body.error};throw s}let o=[...Array.isArray(r.flow.nodes)?r.flow.nodes:[],...Array.isArray(r.flow.configs)?r.flow.configs:[]];for(let s of n){let i=o.find(d=>d.id===s.nodeId);if(!i)return{code:"node_not_found",message:`Node '${s.nodeId}' was not found in flow '${t}'`};if(!(s.field in i))return{code:"field_not_found",message:`Field '${s.field}' was not found on node '${s.nodeId}'`}}return null}a($h,"validateFieldDraftTargets");function Mh(e,t,n,r){if(r)try{let o=vr(r);if(!Array.isArray(o)||o.length===0)throw new Error("targets must be a non-empty array");return{ok:!0,mode:"field",targets:o.map(i=>{if(!i||typeof i!="object")throw new Error("bad target");let d=i,l=typeof d.nodeId=="string"?d.nodeId:typeof d.node=="string"?d.node:"",c=typeof d.field=="string"?d.field:"";if(!l||!c)throw new Error("bad target");return{nodeId:l,field:c}})}}catch{return{ok:!1,status:400,error:"targets must be base64url JSON array of {nodeId,field}",code:"invalid_targets"}}if(n==="json"||!e&&!t)return{ok:!0,mode:"json"};if(!e||!t){let o=He("missing_draft_params");return{ok:!1,status:400,error:"node and field are required for field drafts (or pass mode=json)",code:"missing_draft_params",...o?{hint:o}:{}}}return{ok:!0,mode:"field"}}a(Mh,"planDraftMode");async function Dh(e,t,n,r){try{let o=await $h(t,n,r);if(o){let s=o.code==="node_not_found"||o.code==="field_not_found"?404:400;return j(e,s,o.message,o.code,He(o.code)),!0}}catch(o){let s=W(o);return s?(j(e,s.statusCode,s.body.error,s.body.code,He(s.body.code)),!0):(B(e,o),!0)}return!1}a(Dh,"rejectInvalidFieldTargets");function ol(e,t){return async(n,r)=>{if(Fn(n,r))return;z(r);let o=M(n.query,"flow"),s=M(n.query,"rev"),i=M(n.query,"node"),d=M(n.query,"field"),l=M(n.query,"summary"),c=M(n.query,"title")??l,u=M(n.query,"mode"),p=M(n.query,"targets");if(!o||!s){j(r,400,"flow and rev query parameters are required","missing_draft_params",He("missing_draft_params"));return}if(!n.accessLink){j(r,401,"Access link required","access_link_missing");return}try{let m=await(await Ar(t,s)).getFlow(o);if(m.rev!==s){r.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:s,currentRev:m.rev,hint:He("version_mismatch")});return}}catch(m){let g=W(m);if(g){j(r,g.statusCode,g.body.error,g.body.code,g.body.code==="flow_not_found"?we("flow_not_found","get")??"Use an id from GET ./flows":He(g.body.code));return}B(r,m);return}let f=Mh(i,d,u,p);if(!f.ok){j(r,f.status,f.error,f.code,f.hint);return}if(f.mode==="field"){let m=f.targets&&f.targets.length>0?f.targets:i&&d?[{nodeId:i,field:d}]:[];if(m.length>0&&await Dh(r,t,o,m))return}let h=e.create({linkId:n.accessLink.id,flowId:o,expectedRev:s,mode:f.mode,...i?{nodeId:i}:{},...d?{field:d}:{},...f.targets?{targets:f.targets}:{},...l?{summary:l}:{},...c?{title:c}:{}});r.status(201).json({ok:!0,draftId:h.id,mode:h.mode,expiresAt:new Date(h.expiresAtMs).toISOString(),maxBytes:K,hint:"Upload base64url chunks (~4\u20135 KB raw each; base64 adds ~33%; keep URL under ~8 KB) with GET \u2026/draft/<draftId>/append?seq=N&data=\u2026 then submit with sha256."})}}a(ol,"getLinkDraftNewHandler");function sl(e){return(t,n)=>{if(Fn(t,n))return;z(n);let r=t.params.draftId,o=M(t.query,"seq"),s=M(t.query,"data");if(typeof r!="string"||!r){j(n,400,"Missing draft id","missing_draft_id");return}if(!t.accessLink){j(n,401,"Access link required","access_link_missing");return}if(o===void 0||s===void 0){j(n,400,"seq and data query parameters are required","missing_chunk_params","data must be base64url of the raw chunk bytes.");return}let i=Number(o);try{let d=e.append(r,t.accessLink.id,i,s);n.json({ok:!0,draftId:r,receivedSeqs:d.receivedSeqs,missingSeqs:d.missingSeqs,totalBytes:d.totalBytes,maxBytes:d.maxBytes,hint:d.missingSeqs.length===0?"No gaps so far; call submit when all chunks are uploaded":`Upload missing seqs: ${d.missingSeqs.join(",")}`})}catch(d){if(d instanceof Qt||d instanceof en||d instanceof G){j(n,d.statusCode,d.message,d.code,He(d.code),d instanceof en?{proposalId:d.proposalId}:void 0);return}B(n,d);return}}}a(sl,"getLinkDraftAppendHandler");function Hh(e,t){let n=[];if(e.targets&&e.targets.length>0){let r;try{r=JSON.parse(t)}catch{let o=e.targets[0];if(e.targets.length===1&&o)return[{nodeId:o.nodeId,set:{[o.field]:t}}];throw new G("Multi-target draft body must be JSON","invalid_draft_body")}if(Array.isArray(r)){for(let o of r){let s=o;if(!s.nodeId||!s.field)throw new G("Invalid multi-target entry","invalid_draft_body");n.push({nodeId:s.nodeId,set:{[s.field]:s.value}})}return n}if(r&&typeof r=="object"){let o=r;for(let s of e.targets){let i=`${s.nodeId}.${s.field}`;if(!(i in o)&&!(s.field in o))throw new G(`Missing value for ${i} in draft JSON`,"invalid_draft_body");n.push({nodeId:s.nodeId,set:{[s.field]:i in o?o[i]:o[s.field]}})}return n}throw new G("Invalid multi-target draft JSON","invalid_draft_body")}if(e.nodeId&&e.field)return[{nodeId:e.nodeId,set:{[e.field]:t}}];throw new G("Field draft missing node/field","invalid_draft")}a(Hh,"editsFromFieldDraft");function il(e,t,n,r,o,s){return async(i,d)=>{if(Fn(i,d))return;z(d);let l=i.params.draftId,c=M(i.query,"sha256"),u=M(i.query,"title");if(typeof l!="string"||!l){j(d,400,"Missing draft id","missing_draft_id");return}if(!i.accessLink){j(d,401,"Access link required","access_link_missing");return}if(!c){j(d,400,"sha256 query parameter is required","missing_sha256","Pass sha256 hex of the assembled UTF-8 bytes (before base64url).");return}try{let{draft:p,assembled:f}=e.assembleAndVerify(l,i.accessLink.id,c),h=p.flowId,m,g=p.expectedRev,y=await Ar(r,p.expectedRev);if(p.mode==="field"){let k=Hh(p,f),_=await br(y,p.flowId,k);if(_.expectedRev!==p.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:p.expectedRev,currentRev:_.expectedRev,draftDeleted:!1,hint:"Draft was kept. Re-read the flow and start a new draft with the current rev (or retry if you refresh rev)."});return}m=_.flow,g=_.expectedRev}else{let k=kr(JSON.parse(f),p.flowId);if(h=k.flowId,k.kind==="replace_flow")m={...k.flow,id:k.flow.id||h,type:k.flow.type??"tab",nodes:k.flow.nodes??[]};else{let _=await Gt(y,h,k.ops,{expectedRev:p.expectedRev,...s?{nodeTypes:s}:{}});if(_.expectedRev!==p.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:p.expectedRev,currentRev:_.expectedRev,draftDeleted:!1,hint:"Draft was kept. Re-read the flow and start a new draft with the current rev."});return}m=_.flow,g=_.expectedRev}}let A=u??p.title??p.summary,x=V(i,o),$=ge(i.query),L=await Tt(t,n,i.accessLink,{type:"replace_flow",expectedRev:g,payload:{flowId:h,flow:m},...A?{title:A}:{}},r,x,s);e.markSubmitted(l,i.accessLink.id,L.id);let D={...fe(L,{...x,include:$}),mine:!0},H=ne(t),T=Ve(t,t.get(L.id),i.accessLink.id);d.status(201).json({ok:!0,proposal:D,sha256:(0,rl.createHash)("sha256").update(Buffer.from(f,"utf8")).digest("hex"),...Ye(T),...ue(H),hint:`Waiting for human approval. Tell the user about "${D.displayName}" (${D.reviewUrl}). Poll GET \u2026/proposals/<id> for status.`})}catch(p){if(p instanceof en){j(d,p.statusCode,p.message,p.code,He(p.code),{proposalId:p.proposalId});return}if(p instanceof Qt||p instanceof G){j(d,p.statusCode,p.message,p.code,He(p.code));return}if(p instanceof SyntaxError){j(d,400,"Assembled draft is not valid JSON","invalid_draft_body");return}let f=W(p);if(f){d.status(f.statusCode).json({...f.body,...He(f.body.code)?{hint:He(f.body.code)}:{}});return}B(d,p);return}}}a(il,"getLinkDraftSubmitHandler");ie();tt();_o();zt();Fe();et();Ao();var jh={node_already_exists:"Choose a new node id, or use op:set / op:remove on the existing node.",protected_property:"Omit id, type, and z from set props. wires are allowed when targets exist in the flow.",invalid_wires:"wires must list existing node ids (or add those nodes in the same patch).",invalid_patch:"Pass patch as base64url JSON with nodes[{op,\u2026.}] or type/flow for replace_flow.",invalid_patch_json:"Pass patch as base64url JSON with nodes[{op,\u2026.}] or type/flow for replace_flow.",missing_edits:"Provide set.<prop>=value, find+replace+field, or patch nodes with at least one op.",missing_node_id:"Pass node=<id> with set.<prop>= or find/replace, or include id on each patch.nodes entry.",missing_flow_id:"Pass flow=<id> from GET \u2026/flows.",missing_field:"Pass field=<prop> (e.g. template, func) with find= and replace=.",missing_find:"Pass a non-empty find= string (URL-encoded).",invalid_occurrence:"occurrence must be a 1-based index within the match count.",invalid_replace:"Pass replace= as a string (URL-encoded; empty string is allowed to delete the find text).",conflicting_edits:"Use either find/replace/field or set.<prop>=, not both in one propose URL.",invalid_node_type:"node.type must be a string (not a number or object). Use the palette type name exactly.",invalid_node:"add op needs node as a JSON object: { id, type, \u2026 } \u2014 not a string or array.",z_mismatch:"Each node.z must match the flow id (tab id) you are editing."};function ki(e,t){return we(e,"get",{cause:t})??jh[e]??Tr(e)}a(ki,"proposeErrorHint");function Uh(e,t,n){e.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:t,currentRev:n,hint:ki("version_mismatch")})}a(Uh,"sendVersionMismatch");async function Bh(e,t,n,r,o){let s=kr(vr(t),n),i=s.flowId;if(s.kind==="replace_flow")return{ok:!0,builtFlowId:i,builtFlow:{...s.flow,id:s.flow.id||i,type:s.flow.type??"tab",nodes:s.flow.nodes??[]},expectedRev:r};let d=await Gt(e,i,s.ops,{expectedRev:r,...o?{nodeTypes:o}:{}});return d.expectedRev!==r?{ok:!1,kind:"version_mismatch",expectedRev:r,currentRev:d.expectedRev}:{ok:!0,builtFlowId:i,builtFlow:d.flow,expectedRev:d.expectedRev}}a(Bh,"buildFromPatch");async function qh(e,t,n,r,o){let s=await br(e,t,[{nodeId:n,set:r}]);return s.expectedRev!==o?{ok:!1,kind:"version_mismatch",expectedRev:o,currentRev:s.expectedRev}:{ok:!0,builtFlowId:t,builtFlow:s.flow,expectedRev:s.expectedRev}}a(qh,"buildFromSetProps");async function zh(e,t,n,r,o,s,i,d){let l=await Gt(e,t,[{op:"replace_text",nodeId:n,field:r,find:o,replace:s,...i!==void 0?{occurrence:i}:{}}]);return l.expectedRev!==d?{ok:!1,kind:"version_mismatch",expectedRev:d,currentRev:l.expectedRev}:{ok:!0,builtFlowId:t,builtFlow:l.flow,expectedRev:l.expectedRev}}a(zh,"buildFromReplaceText");function Wh(e){let t=Object.prototype.hasOwnProperty.call(e.query,"replace");return{flowId:M(e.query,"flow"),rev:M(e.query,"rev"),nodeId:M(e.query,"node"),patchEncoded:M(e.query,"patch"),setProps:ii(e.query),findText:M(e.query,"find"),replaceText:t?M(e.query,"replace")??"":void 0,fieldName:M(e.query,"field"),occurrenceRaw:M(e.query,"occurrence")}}a(Wh,"readProposeQuery");function je(e,t,n){j(e,400,n,t,ki(t))}a(je,"jsonProposeError");function Gh(e,t){if(t===void 0||t==="")return{ok:!0};let n=Number(t);return Number.isFinite(n)?{ok:!0,occurrence:Math.trunc(n)}:(je(e,"invalid_occurrence","occurrence must be a positive integer"),{ok:!1})}a(Gh,"parseOccurrenceQuery");async function Kh(e,t,n,r,o){if(n.patchEncoded)return Bh(t,n.patchEncoded,n.flowId,r,o);if(n.findText!==void 0||n.replaceText!==void 0||n.fieldName!==void 0){if(Object.keys(n.setProps).length>0)return je(e,"conflicting_edits","Cannot combine replace_text (find/replace/field) with set.<prop> in one request"),null;if(!n.flowId)return je(e,"missing_flow_id","flow query parameter is required"),null;if(!n.nodeId)return je(e,"missing_node_id","node query parameter is required"),null;if(!n.fieldName)return je(e,"missing_field","field query parameter is required for replace_text"),null;if(n.findText===void 0||n.findText.length===0)return je(e,"missing_find","find query parameter must be a non-empty string"),null;if(n.replaceText===void 0)return je(e,"invalid_replace","replace query parameter is required (may be empty)"),null;let i=Gh(e,n.occurrenceRaw);return i.ok?zh(t,n.flowId,n.nodeId,n.fieldName,n.findText,n.replaceText,i.occurrence,r):null}return n.flowId?n.nodeId?Object.keys(n.setProps).length===0?(je(e,"missing_edits","At least one set.<prop>=value is required"),null):qh(t,n.flowId,n.nodeId,n.setProps,r):(je(e,"missing_node_id","node query parameter is required (or pass patch=)"),null):(je(e,"missing_flow_id","flow query parameter is required"),null)}a(Kh,"buildProposeFromQuery");function al(e,t,n,r,o){return async(s,i)=>{if(Fn(s,i))return;z(i);let d=Wh(s),l=M(s.query,"summary"),c=M(s.query,"title")??l;if(!d.rev){je(i,"missing_expected_rev","rev query parameter is required");return}if(!s.accessLink){j(i,401,"Access link required","access_link_missing");return}try{let u=await Ar(n,d.rev),p=await Kh(i,u,d,d.rev,o);if(!p)return;if(!p.ok){Uh(i,p.expectedRev,p.currentRev);return}let f=V(s,r),h=ge(s.query),m=await Tt(e,t,s.accessLink,{type:"replace_flow",expectedRev:p.expectedRev,payload:{flowId:p.builtFlowId,flow:p.builtFlow},...c?{title:c}:{}},n,f,o),g={...fe(m,{...f,include:h}),mine:!0},y=ne(e),A=Ve(e,e.get(m.id),s.accessLink.id),x=m.warnings;i.status(201).json({ok:!0,proposal:g,...l?{summary:l}:{},...Ye(A),...ue(y),...x&&x.length>0?{warnings:x}:{},hint:`Waiting for human approval. Tell the user about "${g.displayName}" (${g.reviewUrl}). Poll GET \u2026/proposals/<id> for status.${x&&x.length>0?` Soft warnings (${x.length}) are non-blocking \u2014 configure later if needed.`:""}`})}catch(u){let p=W(u,"get");if(p){let f=ki(p.body.code,p.body.cause);i.status(p.statusCode).json({...p.body,...f?{hint:f}:{}});return}B(i,u)}}}a(al,"getLinkProposeHandler");ie();tt();ie();var Jh=50,dl=40,Vh=20;function Yh(e){return Buffer.byteLength(JSON.stringify(e),"utf8")}a(Yh,"nodeBytes");function Xh(e){let t={id:e.id,type:e.type,bytes:Yh(e)};return typeof e.name=="string"&&(t.name=e.name),"wires"in e&&(t.wires=e.wires),t}a(Xh,"summarizeNode");async function Do(e,t){let{rev:n,flow:r}=await Me(e,t),o=r,s=Array.isArray(o.nodes)?o.nodes.map(Xh):[],i={rev:n,flowId:o.id||t,nodes:s,nodeCount:s.length};return typeof o.label=="string"&&(i.label=o.label),typeof o.type=="string"&&(i.type=o.type),i}a(Do,"getFlowSummary");function ll(e){let t=[];for(let n of e){if(!n||typeof n!="object"||Array.isArray(n))continue;let r=n;(r.type==="tab"||r.type==="subflow"||Array.isArray(r.nodes))&&t.push(r)}return t}a(ll,"collectTabs");async function cl(e,t,n){if(!t)throw new b("Missing node id","missing_node_id");if(n){let{rev:d,flow:l}=await Me(e,n),c=l,u=Array.isArray(c.nodes)?c.nodes:[],p=Array.isArray(c.configs)?c.configs:[],f=[...u,...p].find(h=>h.id===t);if(!f)throw new b("Node was not found","node_not_found",{statusCode:404});return{rev:d,flowId:n,node:f}}let{rev:r,flows:o}=await ut(e),s,i=null;for(let d of o){if(!d||typeof d!="object"||Array.isArray(d))continue;let l=d;if(l.id===t&&typeof l.type=="string"&&l.type!=="tab"&&l.type!=="subflow"){s=l,i=typeof l.z=="string"&&l.z?l.z:null;break}}if(!s)for(let d of ll(o)){let l=d.id;if(l)try{let u=(await Me(e,l)).flow,f=[...u.nodes??[],...u.configs??[]].find(h=>h.id===t);if(f){s=f,i=l;break}}catch{}}if(!s)throw new b("Node was not found","node_not_found",{statusCode:404});return{rev:r,flowId:i,node:s}}a(cl,"findNodeInFlows");async function Ho(e,t,n){let r=await cl(e,t,n);return{rev:r.rev,flowId:r.flowId,node:r.node}}a(Ho,"getNodeById");function Zh(e){if(e===void 0||e==="")return;let t=/^(\d+)(?:-(\d+))?$/.exec(e.trim());if(!t)throw new b("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");let n=Number(t[1]),r=t[2]!==void 0?Number(t[2]):n;if(!Number.isInteger(n)||!Number.isInteger(r)||n<1)throw new b("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");if(r<n)throw new b("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");return{start:n,end:r}}a(Zh,"parseLineRange");async function jo(e,t,n,r={}){if(!n)throw new b("Missing field name","missing_field");let o=await cl(e,t,r.flowId);if(!(n in o.node))throw new b("Field was not found on the node","field_not_found");let s=o.node[n];if(typeof s!="string")throw new b("Field is not a string","field_not_string");let i=s.split(`
|
|
985
|
-
`),d=i.length,l=Buffer.byteLength(s,"utf8"),c=
|
|
986
|
-
`);return{rev:o.rev,flowId:o.flowId,nodeId:t,field:n,value:f,totalLines:d,bytes:l,lineRange:{start:c.start,end:Math.min(c.end,d)}}}a(
|
|
983
|
+
`}a(Vh,"docsErrorsAndAlso");function cl(e){let{link:t,openapiPath:n,restApiBase:r,mcpRpcPath:o,publicOrigin:s}=e,i=Kn(t.expiresAt),d=t.label?` (${Kn(t.label)})`:"",l=Kn(n),c=Kn(r),u=Kn(o);return[Uh(d,i),jh(c,l,u,s?Kn(`${s}${o}`):ll),Bh(),qh(),Wh(),Kh(),zh(),Gh(),Jh(),Vh()].join(`
|
|
984
|
+
`)}a(cl,"renderGetLinkDocs");y();var kl=require("crypto");de();le();ct();en();qe();lt();y();le();function Wo(e){return{error:e instanceof Error&&e.message.trim()?e.message:"Internal server error",code:"internal_error"}}a(Wo,"internalErrorBody");function Yh(e,t){e.headersSent||e.status(500).json(Wo(t))}a(Yh,"sendInternalError");function z(e,t){if(e.headersSent)return;let n=J(t);if(n){e.status(n.statusCode).json(n.body);return}Yh(e,t)}a(z,"sendCaughtRouteError");function D(e){return(t,n,r)=>{Promise.resolve().then(()=>e(t,n,r)).catch(o=>{if(n.headersSent){r(o instanceof Error?o:new Error(String(o)));return}z(n,o)})}}a(D,"asyncRoute");y();var Go=require("crypto");de();Re();var V=class extends Error{static{a(this,"DraftValidationError")}statusCode=400;code;constructor(t,n="draft_invalid"){super(t),this.name="DraftValidationError",this.code=n}},ln=class extends Error{static{a(this,"DraftNotFoundError")}statusCode=404;code="draft_not_found";constructor(t){super(`Draft '${t}' was not found or has expired`),this.name="DraftNotFoundError"}},cn=class extends Error{static{a(this,"DraftAlreadySubmittedError")}statusCode=409;code="draft_already_submitted";proposalId;constructor(t,n){super(`Draft '${t}' was already submitted as proposal '${n}'`),this.name="DraftAlreadySubmittedError",this.proposalId=n}},Zh=/^[A-Za-z0-9_-]+={0,2}$/;function ul(e){if(!Zh.test(e)||e.replace(/=+$/,"").length===0)throw new V("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data");let t;try{t=Buffer.from(e,"base64url")}catch{throw new V("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data")}if(t.byteLength===0&&e.length>0)throw new V("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data");return t}a(ul,"assertValidBase64UrlChunk");var zo=class{static{a(this,"DraftStore")}drafts=new Map;submitted=new Map;now;randomId;maxBytes;ttlMs;constructor(t={}){this.now=t.now??(()=>Date.now()),this.randomId=t.randomId??(()=>(0,Go.randomBytes)(12).toString("hex")),this.maxBytes=t.maxBytes??Y,this.ttlMs=t.ttlMs??Ca}purgeExpired(t){for(let[n,r]of this.drafts)r.expiresAtMs<=t&&this.drafts.delete(n);for(let[n,r]of this.submitted)r.expiresAtMs<=t&&this.submitted.delete(n)}create(t){let n=this.now();this.purgeExpired(n);let r=this.randomId(),o={id:r,linkId:t.linkId,flowId:t.flowId,expectedRev:t.expectedRev,mode:t.mode,chunks:new Map,createdAtMs:n,expiresAtMs:n+this.ttlMs,totalBytes:0};return t.nodeId!==void 0&&(o.nodeId=t.nodeId),t.field!==void 0&&(o.field=t.field),t.targets!==void 0&&(o.targets=t.targets),t.summary!==void 0&&(o.summary=t.summary),t.title!==void 0&&(o.title=t.title),this.drafts.set(r,o),o}getForLink(t,n){let r=this.now();this.purgeExpired(r);let o=this.submitted.get(t);if(o?.linkId===n)throw new cn(t,o.proposalId);let s=this.drafts.get(t);if(s?.linkId!==n)throw new ln(t);if(s.expiresAtMs<=r)throw new ln(t);return s}markSubmitted(t,n,r){let o=this.now();this.drafts.delete(t),this.submitted.set(t,{linkId:n,proposalId:r,expiresAtMs:o+this.ttlMs})}append(t,n,r,o){if(!Number.isInteger(r)||r<0)throw new V("seq must be a non-negative integer","invalid_seq");if(typeof o!="string")throw new V("data must be a base64url string","invalid_chunk");let s=this.getForLink(t,n),i=s.chunks.get(r);if(i!==void 0){if(i!==o)throw new V(`Chunk seq ${r} already stored with different data`,"chunk_conflict")}else{let u=ul(o),p=s.totalBytes+u.byteLength;if(p>this.maxBytes)throw new V(`Draft exceeds limit of ${this.maxBytes} bytes (decoded)`,"payload_too_large");s.chunks.set(r,o),s.totalBytes=p}let d=[...s.chunks.keys()].sort((u,p)=>u-p),l=[],c=d.at(-1);if(c!==void 0)for(let u=0;u<=c;u+=1)s.chunks.has(u)||l.push(u);return{draft:s,receivedSeqs:d,missingSeqs:l,totalBytes:s.totalBytes,maxBytes:this.maxBytes}}getMaxBytes(){return this.maxBytes}assembleAndVerify(t,n,r){let o=this.getForLink(t,n);if(o.chunks.size===0)throw new V("Draft has no chunks","draft_empty");let i=[...o.chunks.keys()].sort((h,m)=>h-m).at(-1);if(i===void 0)throw new V("Draft has no chunks","draft_empty");let d=[];for(let h=0;h<=i;h+=1)o.chunks.has(h)||d.push(h);if(d.length>0)throw new V(`Missing chunk seqs: ${d.join(",")}`,"missing_chunks");let l=[];for(let h=0;h<=i;h+=1){let m=o.chunks.get(h);if(m===void 0)throw new V(`Missing chunk seqs: ${h}`,"missing_chunks");l.push(m)}let c=l.map(h=>ul(h)),u=Buffer.concat(c);if(u.byteLength>this.maxBytes)throw new V(`Assembled draft exceeds limit of ${this.maxBytes} bytes`,"payload_too_large");let p=r.trim().toLowerCase();if(!/^[0-9a-f]{64}$/.test(p))throw new V("invalid_sha256: expected 64 hex chars","invalid_sha256");if((0,Go.createHash)("sha256").update(u).digest("hex")!==p)throw new V("sha256 does not match assembled draft content","draft_hash_mismatch");return{draft:o,assembled:u.toString("utf8")}}size(){return this.purgeExpired(this.now()),this.drafts.size}clear(){this.drafts.clear(),this.submitted.clear()}};No();y();Gt();Re();y();var Dt=rr(require("express-rate-limit"),1);var $i=6e4,pl=600,fl=300;function Oi(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?Math.max(1,Math.floor(e)):t}a(Oi,"positiveInt");function xt(e){if(!e)return{windowMs:$i,maxPerIp:pl,maxPerToken:fl};let t=e;return{windowMs:Oi(t.windowMs??t.rateLimitWindowMs,$i),maxPerIp:Oi(t.maxPerIp??t.rateLimitMaxPerIp,pl),maxPerToken:Oi(t.maxPerToken??t.rateLimitMaxPerToken,fl)}}a(xt,"resolveRateLimitConfig");function Xh(e,t){let r=e.rateLimit?.resetTime;return r instanceof Date&&!Number.isNaN(r.getTime())?Math.max(1,Math.ceil((r.getTime()-Date.now())/1e3)):Math.max(1,Math.ceil(t/1e3))}a(Xh,"retryAfterSeconds");function hl(e,t,n=$i){let r=Xh(e,n);t.setHeader("Retry-After",String(r)),t.setHeader("Cache-Control","no-store"),t.status(429).json({error:"Too many requests. Slow down and retry.",code:"rate_limited",retryAfter:r,hint:"Wait retryAfter seconds, then retry. Prefer summary/search reads over full field downloads."})}a(hl,"sendRateLimited");function Qh(e){return(t,n,r,o)=>{hl(t,n,e)}}a(Qh,"rateLimitedHandler");function Ko(e){return`ip:${(0,Dt.ipKeyGenerator)(e.ip??"unknown")}`}a(Ko,"clientIpKey");function Jo(e){let t=e.params.token;if(typeof t=="string"&&t.length>0)return`token:${t.slice(0,48)}`;let n=e.get("authorization")??e.headers.authorization;if(typeof n=="string"){let s=/^Bearer\s+(\S+)/i.exec(n.trim())?.[1];if(s&&(s.startsWith("aia_")||s.length>=24))return`token:${s.slice(0,48)}`}let r=e;return r.accessLink?.id?`token:link:${r.accessLink.id}`:null}a(Jo,"accessTokenRateLimitKey");function ml(e,t,n,r){let o={windowMs:t,max:e,standardHeaders:!0,legacyHeaders:!1,keyGenerator:n,handler:Qh(t)};return r&&(o.skip=r),(0,Dt.default)(o)}a(ml,"createLimiter");function Mi(e){let t=xt(e);return ml(t.maxPerIp,t.windowMs,Ko)}a(Mi,"createAiAccessRateLimiter");function em(e){let t=xt(e);return ml(t.maxPerToken,t.windowMs,n=>Jo(n)??Ko(n),n=>Jo(n)==null)}a(em,"createAiAccessTokenRateLimiter");function gl(e){let t=xt(e),n=Mi(t),r=em(t);return{ip:n,token:r,stack:[n,r],config:t}}a(gl,"createAiAccessDualRateLimiters");function yl(e){let t=xt(e()).windowMs,n=xt(e()).maxPerIp,r=xt(e()).maxPerToken,o=new Dt.MemoryStore,s=new Dt.MemoryStore,i=a(()=>{let m=xt(e());if(m.windowMs!==t||m.maxPerIp!==n||m.maxPerToken!==r){let g=m.windowMs!==t;t=m.windowMs,n=m.maxPerIp,r=m.maxPerToken,o.resetAll(),s.resetAll(),g&&(o.init?.({windowMs:t}),s.init?.({windowMs:t}))}return m},"refreshWindow"),d=new WeakSet,l=a(m=>{if(d.has(m))return;d.add(m);let g=m.setHeader.bind(m);m.setHeader=((w,v)=>(w.toLowerCase()==="ratelimit-policy"&&typeof v=="string"&&(v=v.replace(/;w=\d+/,`;w=${Math.ceil(t/1e3)}`)),g(w,v)))},"useLiveWindowPolicy"),c=a((m,g)=>(0,Dt.default)({store:g,windowMs:t,limit:a(()=>{let w=xt(e());return m?w.maxPerToken:w.maxPerIp},"limit"),standardHeaders:!0,legacyHeaders:!1,keyGenerator:m?w=>Jo(w)??Ko(w):Ko,skip:a(w=>m&&Jo(w)==null,"skip"),handler:a((w,v,k)=>{hl(w,v,xt(e()).windowMs)},"handler")}),"make"),u=c(!1,o),p=c(!0,s);return{ip:a((m,g,w)=>(i(),l(g),u(m,g,w)),"ip"),token:a((m,g,w)=>(i(),l(g),p(m,g,w)),"token")}}a(yl,"createLiveAiAccessDualRateLimiters");Te();function K(e){e.setHeader("Cache-Control","no-store"),e.setHeader("Pragma","no-cache")}a(K,"setNoStore");function Di(e){let t=e.originalUrl||e.url||"",n=Os(t);n!==t&&(Object.defineProperty(e,"originalUrl",{value:n,writable:!0,configurable:!0}),typeof e.url=="string"&&Object.defineProperty(e,"url",{value:Os(e.url),writable:!0,configurable:!0}))}a(Di,"redactTokenFromRequestUrl");function wl(e,t){return(n,r,o)=>{K(r);let s=n.originalUrl||n.url||"";if(n.getLinkUrlBytes=Buffer.byteLength(s,"utf8"),Di(n),!De(e.settings)){r.status(403).json({error:"AI Access features are disabled. Enable adminAuth, or set aiAccess.allowWithoutAdminAuth to true for local-only use.",code:"ai_features_disabled",hint:"Ask the Node-RED owner to enable AI Access."});return}let i=n.params.token;if(typeof i!="string"||i.length===0){r.setHeader("WWW-Authenticate",ge),r.status(401).json({error:He,code:ht,hint:"Ask the user for a new self-contained access link URL."});return}let d=i;try{d=decodeURIComponent(i)}catch{}let l=t.validateResult(d);if(l.status==="expired"){fr(n,r,e.settings.httpAdminRoot,l.expiresAt);return}if(l.status!=="active"){let c=d.length<24||d.startsWith("aia_")&&d.length<40?"The access-link URL may be truncated \u2014 ask the user for the full /mcp/a/<token> link.":"Ask the user for a new self-contained access link URL (it may have expired, or Node-RED may have restarted).";r.setHeader("WWW-Authenticate",ge),r.status(401).json({error:zt,code:pr,hint:c});return}if(l.link.kind!=="get_link"){hr(r,"get_link",l.link.kind);return}n.accessLink=l.link,n.getLinkToken=d,o()}}a(wl,"requireGetLinkToken");function Jn(e,t){let n=e.getLinkUrlBytes;return(typeof n=="number"?n:Buffer.byteLength(e.originalUrl||e.url||"","utf8"))>Tn?(K(t),t.status(414).json({error:`Request URL exceeds ${Tn} bytes`,code:"url_too_long",hint:"Use chunked drafts: GET \u2026/draft/new then \u2026/draft/<id>/append?seq=&data= then \u2026/draft/<id>/submit?sha256=, or use POST /mcp/api/v1/proposals with Authorization: Bearer <token>.",limitBytes:Tn}),!0):!1}a(Jn,"rejectOversizedGetUrl");function B(e,t,n,r,o,s){K(e),e.status(t).json({error:n,code:r,...o?{hint:o}:{},...s})}a(B,"jsonError");function bl(){return(e,t)=>{Di(e),K(t),t.status(404).json({error:"Not found",code:"not_found"})}}a(bl,"getLinkNotFoundHandler");var tm="link_create_not_supported";function vl(){return(e,t)=>{Di(e),K(t),t.status(403).json({error:"GET access links cannot create tokens",code:tm,hint:"Ask an admin with * permission to mint tokens via POST /mcp/api/v1/links on the /ai-access dashboard (Access tokens tab). This get_link URL cannot create, list, or revoke tokens."})}}a(vl,"getLinkMintNotSupportedHandler");y();function j(e,t){let n=e[t];if(typeof n=="string"&&n.length>0)return n;if(Array.isArray(n)&&typeof n[0]=="string"&&n[0].length>0)return n[0]}a(j,"queryString");function Ke(e){switch(e){case"draft_not_found":return"Start a new draft with GET \u2026/draft/new.";case"draft_already_submitted":return"This draft already created a proposal; poll that proposal or start a new draft.";case"chunk_conflict":return"Re-send the same data for that seq, or start a new draft.";case"payload_too_large":return"Shrink the content; totalBytes counts decoded bytes against maxBytes.";case"invalid_seq":return"seq must be a non-negative integer (0, 1, 2, \u2026).";case"invalid_chunk":case"invalid_chunk_data":return"data must be base64url of the raw chunk bytes (A-Z, a-z, 0-9, -, _).";case"missing_chunks":return"Upload every seq from 0 through max before submit.";case"invalid_sha256":return"sha256 must be exactly 64 hexadecimal characters.";case"draft_hash_mismatch":return"sha256 must be the hex digest of the assembled UTF-8 bytes (before base64url).";case"draft_empty":return"Append at least one chunk before submit.";case"node_not_found":return"Call GET \u2026/flows/<id> and use a node id from that response (or use mode=json with op:add).";case"field_not_found":return"Use a property that exists on the node, or mode=json to add/replace nodes.";case"missing_draft_params":return"Example: \u2026/draft/new?flow=tab1&rev=abc&node=n1&field=template";case"version_mismatch":return"Re-read the flow and start a new draft with the current rev.";case"protected_property":return"Omit id, type, and z from set props. wires are allowed when targets exist in the flow.";case"invalid_wires":return"wires must list existing node ids (or add those nodes in the same patch).";default:return}}a(Ke,"draftErrorHint");async function nm(e,t,n){let r;try{r=await e.getFlow(t)}catch(s){let i=J(s);if(i)return{code:i.body.code,message:i.body.error};throw s}let o=[...Array.isArray(r.flow.nodes)?r.flow.nodes:[],...Array.isArray(r.flow.configs)?r.flow.configs:[]];for(let s of n){let i=o.find(d=>d.id===s.nodeId);if(!i)return{code:"node_not_found",message:`Node '${s.nodeId}' was not found in flow '${t}'`};if(!(s.field in i))return{code:"field_not_found",message:`Field '${s.field}' was not found on node '${s.nodeId}'`}}return null}a(nm,"validateFieldDraftTargets");function rm(e,t,n,r){if(r)try{let o=Er(r);if(!Array.isArray(o)||o.length===0)throw new Error("targets must be a non-empty array");return{ok:!0,mode:"field",targets:o.map(i=>{if(!i||typeof i!="object")throw new Error("bad target");let d=i,l=typeof d.nodeId=="string"?d.nodeId:typeof d.node=="string"?d.node:"",c=typeof d.field=="string"?d.field:"";if(!l||!c)throw new Error("bad target");return{nodeId:l,field:c}})}}catch{return{ok:!1,status:400,error:"targets must be base64url JSON array of {nodeId,field}",code:"invalid_targets"}}if(n==="json"||!e&&!t)return{ok:!0,mode:"json"};if(!e||!t){let o=Ke("missing_draft_params");return{ok:!1,status:400,error:"node and field are required for field drafts (or pass mode=json)",code:"missing_draft_params",...o?{hint:o}:{}}}return{ok:!0,mode:"field"}}a(rm,"planDraftMode");async function om(e,t,n,r){try{let o=await nm(t,n,r);if(o){let s=o.code==="node_not_found"||o.code==="field_not_found"?404:400;return B(e,s,o.message,o.code,Ke(o.code)),!0}}catch(o){let s=J(o);return s?(B(e,s.statusCode,s.body.error,s.body.code,Ke(s.body.code)),!0):(z(e,o),!0)}return!1}a(om,"rejectInvalidFieldTargets");function Al(e,t){return async(n,r)=>{if(Jn(n,r))return;K(r);let o=j(n.query,"flow"),s=j(n.query,"rev"),i=j(n.query,"node"),d=j(n.query,"field"),l=j(n.query,"summary"),c=j(n.query,"title")??l,u=j(n.query,"mode"),p=j(n.query,"targets");if(!o||!s){B(r,400,"flow and rev query parameters are required","missing_draft_params",Ke("missing_draft_params"));return}if(!n.accessLink){B(r,401,"Access link required","access_link_missing");return}try{let m=await(await Lr(t,s)).getFlow(o);if(m.rev!==s){r.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:s,currentRev:m.rev,hint:Ke("version_mismatch")});return}}catch(m){let g=J(m);if(g){B(r,g.statusCode,g.body.error,g.body.code,g.body.code==="flow_not_found"?Ae("flow_not_found","get")??"Use an id from GET ./flows":Ke(g.body.code));return}z(r,m);return}let f=rm(i,d,u,p);if(!f.ok){B(r,f.status,f.error,f.code,f.hint);return}if(f.mode==="field"){let m=f.targets&&f.targets.length>0?f.targets:i&&d?[{nodeId:i,field:d}]:[];if(m.length>0&&await om(r,t,o,m))return}let h=e.create({linkId:n.accessLink.id,flowId:o,expectedRev:s,mode:f.mode,...i?{nodeId:i}:{},...d?{field:d}:{},...f.targets?{targets:f.targets}:{},...l?{summary:l}:{},...c?{title:c}:{}});r.status(201).json({ok:!0,draftId:h.id,mode:h.mode,expiresAt:new Date(h.expiresAtMs).toISOString(),maxBytes:Y,hint:"Upload base64url chunks (~4\u20135 KB raw each; base64 adds ~33%; keep URL under ~8 KB) with GET \u2026/draft/<draftId>/append?seq=N&data=\u2026 then submit with sha256."})}}a(Al,"getLinkDraftNewHandler");function xl(e){return(t,n)=>{if(Jn(t,n))return;K(n);let r=t.params.draftId,o=j(t.query,"seq"),s=j(t.query,"data");if(typeof r!="string"||!r){B(n,400,"Missing draft id","missing_draft_id");return}if(!t.accessLink){B(n,401,"Access link required","access_link_missing");return}if(o===void 0||s===void 0){B(n,400,"seq and data query parameters are required","missing_chunk_params","data must be base64url of the raw chunk bytes.");return}let i=Number(o);try{let d=e.append(r,t.accessLink.id,i,s);n.json({ok:!0,draftId:r,receivedSeqs:d.receivedSeqs,missingSeqs:d.missingSeqs,totalBytes:d.totalBytes,maxBytes:d.maxBytes,hint:d.missingSeqs.length===0?"No gaps so far; call submit when all chunks are uploaded":`Upload missing seqs: ${d.missingSeqs.join(",")}`})}catch(d){if(d instanceof ln||d instanceof cn||d instanceof V){B(n,d.statusCode,d.message,d.code,Ke(d.code),d instanceof cn?{proposalId:d.proposalId}:void 0);return}z(n,d);return}}}a(xl,"getLinkDraftAppendHandler");function sm(e,t){let n=[];if(e.targets&&e.targets.length>0){let r;try{r=JSON.parse(t)}catch{let o=e.targets[0];if(e.targets.length===1&&o)return[{nodeId:o.nodeId,set:{[o.field]:t}}];throw new V("Multi-target draft body must be JSON","invalid_draft_body")}if(Array.isArray(r)){for(let o of r){let s=o;if(!s.nodeId||!s.field)throw new V("Invalid multi-target entry","invalid_draft_body");n.push({nodeId:s.nodeId,set:{[s.field]:s.value}})}return n}if(r&&typeof r=="object"){let o=r;for(let s of e.targets){let i=`${s.nodeId}.${s.field}`;if(!(i in o)&&!(s.field in o))throw new V(`Missing value for ${i} in draft JSON`,"invalid_draft_body");n.push({nodeId:s.nodeId,set:{[s.field]:i in o?o[i]:o[s.field]}})}return n}throw new V("Invalid multi-target draft JSON","invalid_draft_body")}if(e.nodeId&&e.field)return[{nodeId:e.nodeId,set:{[e.field]:t}}];throw new V("Field draft missing node/field","invalid_draft")}a(sm,"editsFromFieldDraft");function Sl(e,t,n,r,o,s){return async(i,d)=>{if(Jn(i,d))return;K(d);let l=i.params.draftId,c=j(i.query,"sha256"),u=j(i.query,"title");if(typeof l!="string"||!l){B(d,400,"Missing draft id","missing_draft_id");return}if(!i.accessLink){B(d,401,"Access link required","access_link_missing");return}if(!c){B(d,400,"sha256 query parameter is required","missing_sha256","Pass sha256 hex of the assembled UTF-8 bytes (before base64url).");return}try{let{draft:p,assembled:f}=e.assembleAndVerify(l,i.accessLink.id,c),h=p.flowId,m,g=p.expectedRev,w=await Lr(r,p.expectedRev);if(p.mode==="field"){let S=sm(p,f),F=await Cr(w,p.flowId,S);if(F.expectedRev!==p.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:p.expectedRev,currentRev:F.expectedRev,draftDeleted:!1,hint:"Draft was kept. Re-read the flow and start a new draft with the current rev (or retry if you refresh rev)."});return}m=F.flow,g=F.expectedRev}else{let S=Ir(JSON.parse(f),p.flowId);if(h=S.flowId,S.kind==="replace_flow")m={...S.flow,id:S.flow.id||h,type:S.flow.type??"tab",nodes:S.flow.nodes??[]};else{let F=await tn(w,h,S.ops,{expectedRev:p.expectedRev,...s?{nodeTypes:s}:{}});if(F.expectedRev!==p.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:p.expectedRev,currentRev:F.expectedRev,draftDeleted:!1,hint:"Draft was kept. Re-read the flow and start a new draft with the current rev."});return}m=F.flow,g=F.expectedRev}}let v=u??p.title??p.summary,k=Z(i,o),$=ve(i.query),C=await Ot(t,n,i.accessLink,{type:"replace_flow",expectedRev:g,payload:{flowId:h,flow:m},...v?{title:v}:{}},r,k,s);e.markSubmitted(l,i.accessLink.id,C.id);let O={...ue(C,{...k,include:$}),mine:!0},q=se(t),T=it(t,t.get(C.id),i.accessLink.id);d.status(201).json({ok:!0,proposal:O,sha256:(0,kl.createHash)("sha256").update(Buffer.from(f,"utf8")).digest("hex"),...at(T),...me(q),hint:`Waiting for human approval. Tell the user about "${O.displayName}" (${O.reviewUrl}). Poll GET \u2026/proposals/<id> for status.`})}catch(p){if(p instanceof cn){B(d,p.statusCode,p.message,p.code,Ke(p.code),{proposalId:p.proposalId});return}if(p instanceof ln||p instanceof V){B(d,p.statusCode,p.message,p.code,Ke(p.code));return}if(p instanceof SyntaxError){B(d,400,"Assembled draft is not valid JSON","invalid_draft_body");return}let f=J(p);if(f){d.status(f.statusCode).json({...f.body,...Ke(f.body.code)?{hint:Ke(f.body.code)}:{}});return}z(d,p);return}}}a(Sl,"getLinkDraftSubmitHandler");y();le();ct();Do();en();qe();lt();No();var im={node_already_exists:"Choose a new node id, or use op:set / op:remove on the existing node.",protected_property:"Omit id, type, and z from set props. wires are allowed when targets exist in the flow.",invalid_wires:"wires must list existing node ids (or add those nodes in the same patch).",invalid_patch:"Pass patch as base64url JSON with nodes[{op,\u2026.}] or type/flow for replace_flow.",invalid_patch_json:"Pass patch as base64url JSON with nodes[{op,\u2026.}] or type/flow for replace_flow.",missing_edits:"Provide set.<prop>=value, find+replace+field, or patch nodes with at least one op.",missing_node_id:"Pass node=<id> with set.<prop>= or find/replace, or include id on each patch.nodes entry.",missing_flow_id:"Pass flow=<id> from GET \u2026/flows.",missing_field:"Pass field=<prop> (e.g. template, func) with find= and replace=.",missing_find:"Pass a non-empty find= string (URL-encoded).",invalid_occurrence:"occurrence must be a 1-based index within the match count.",invalid_replace:"Pass replace= as a string (URL-encoded; empty string is allowed to delete the find text).",conflicting_edits:"Use either find/replace/field or set.<prop>=, not both in one propose URL.",invalid_node_type:"node.type must be a string (not a number or object). Use the palette type name exactly.",invalid_node:"add op needs node as a JSON object: { id, type, \u2026 } \u2014 not a string or array.",z_mismatch:"Each node.z must match the flow id (tab id) you are editing."};function Hi(e,t){return Ae(e,"get",{cause:t})??im[e]??Or(e)}a(Hi,"proposeErrorHint");function am(e,t,n){e.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:t,currentRev:n,hint:Hi("version_mismatch")})}a(am,"sendVersionMismatch");async function dm(e,t,n,r,o){let s=Ir(Er(t),n),i=s.flowId;if(s.kind==="replace_flow")return{ok:!0,builtFlowId:i,builtFlow:{...s.flow,id:s.flow.id||i,type:s.flow.type??"tab",nodes:s.flow.nodes??[]},expectedRev:r};let d=await tn(e,i,s.ops,{expectedRev:r,...o?{nodeTypes:o}:{}});return d.expectedRev!==r?{ok:!1,kind:"version_mismatch",expectedRev:r,currentRev:d.expectedRev}:{ok:!0,builtFlowId:i,builtFlow:d.flow,expectedRev:d.expectedRev}}a(dm,"buildFromPatch");async function lm(e,t,n,r,o){let s=await Cr(e,t,[{nodeId:n,set:r}]);return s.expectedRev!==o?{ok:!1,kind:"version_mismatch",expectedRev:o,currentRev:s.expectedRev}:{ok:!0,builtFlowId:t,builtFlow:s.flow,expectedRev:s.expectedRev}}a(lm,"buildFromSetProps");async function cm(e,t,n,r,o,s,i,d){let l=await tn(e,t,[{op:"replace_text",nodeId:n,field:r,find:o,replace:s,...i!==void 0?{occurrence:i}:{}}]);return l.expectedRev!==d?{ok:!1,kind:"version_mismatch",expectedRev:d,currentRev:l.expectedRev}:{ok:!0,builtFlowId:t,builtFlow:l.flow,expectedRev:l.expectedRev}}a(cm,"buildFromReplaceText");function um(e){let t=Object.prototype.hasOwnProperty.call(e.query,"replace");return{flowId:j(e.query,"flow"),rev:j(e.query,"rev"),nodeId:j(e.query,"node"),patchEncoded:j(e.query,"patch"),setProps:ki(e.query),findText:j(e.query,"find"),replaceText:t?j(e.query,"replace")??"":void 0,fieldName:j(e.query,"field"),occurrenceRaw:j(e.query,"occurrence")}}a(um,"readProposeQuery");function Je(e,t,n){B(e,400,n,t,Hi(t))}a(Je,"jsonProposeError");function pm(e,t){if(t===void 0||t==="")return{ok:!0};let n=Number(t);return Number.isFinite(n)?{ok:!0,occurrence:Math.trunc(n)}:(Je(e,"invalid_occurrence","occurrence must be a positive integer"),{ok:!1})}a(pm,"parseOccurrenceQuery");async function fm(e,t,n,r,o){if(n.patchEncoded)return dm(t,n.patchEncoded,n.flowId,r,o);if(n.findText!==void 0||n.replaceText!==void 0||n.fieldName!==void 0){if(Object.keys(n.setProps).length>0)return Je(e,"conflicting_edits","Cannot combine replace_text (find/replace/field) with set.<prop> in one request"),null;if(!n.flowId)return Je(e,"missing_flow_id","flow query parameter is required"),null;if(!n.nodeId)return Je(e,"missing_node_id","node query parameter is required"),null;if(!n.fieldName)return Je(e,"missing_field","field query parameter is required for replace_text"),null;if(n.findText===void 0||n.findText.length===0)return Je(e,"missing_find","find query parameter must be a non-empty string"),null;if(n.replaceText===void 0)return Je(e,"invalid_replace","replace query parameter is required (may be empty)"),null;let i=pm(e,n.occurrenceRaw);return i.ok?cm(t,n.flowId,n.nodeId,n.fieldName,n.findText,n.replaceText,i.occurrence,r):null}return n.flowId?n.nodeId?Object.keys(n.setProps).length===0?(Je(e,"missing_edits","At least one set.<prop>=value is required"),null):lm(t,n.flowId,n.nodeId,n.setProps,r):(Je(e,"missing_node_id","node query parameter is required (or pass patch=)"),null):(Je(e,"missing_flow_id","flow query parameter is required"),null)}a(fm,"buildProposeFromQuery");function Rl(e,t,n,r,o){return async(s,i)=>{if(Jn(s,i))return;K(i);let d=um(s),l=j(s.query,"summary"),c=j(s.query,"title")??l;if(!d.rev){Je(i,"missing_expected_rev","rev query parameter is required");return}if(!s.accessLink){B(i,401,"Access link required","access_link_missing");return}try{let u=await Lr(n,d.rev),p=await fm(i,u,d,d.rev,o);if(!p)return;if(!p.ok){am(i,p.expectedRev,p.currentRev);return}let f=Z(s,r),h=ve(s.query),m=await Ot(e,t,s.accessLink,{type:"replace_flow",expectedRev:p.expectedRev,payload:{flowId:p.builtFlowId,flow:p.builtFlow},...c?{title:c}:{}},n,f,o),g={...ue(m,{...f,include:h}),mine:!0},w=se(e),v=it(e,e.get(m.id),s.accessLink.id),k=m.warnings;i.status(201).json({ok:!0,proposal:g,...l?{summary:l}:{},...at(v),...me(w),...k&&k.length>0?{warnings:k}:{},hint:`Waiting for human approval. Tell the user about "${g.displayName}" (${g.reviewUrl}). Poll GET \u2026/proposals/<id> for status.${k&&k.length>0?` Soft warnings (${k.length}) are non-blocking \u2014 configure later if needed.`:""}`})}catch(u){let p=J(u,"get");if(p){let f=Hi(p.body.code,p.body.cause);i.status(p.statusCode).json({...p.body,...f?{hint:f}:{}});return}z(i,u)}}}a(Rl,"getLinkProposeHandler");y();le();ct();y();le();var hm=50,Tl=40,mm=20;function gm(e){return Buffer.byteLength(JSON.stringify(e),"utf8")}a(gm,"nodeBytes");function ym(e){let t={id:e.id,type:e.type,bytes:gm(e)};return typeof e.name=="string"&&(t.name=e.name),"wires"in e&&(t.wires=e.wires),t}a(ym,"summarizeNode");async function Vo(e,t){let{rev:n,flow:r}=await Ge(e,t),o=r,s=Array.isArray(o.nodes)?o.nodes.map(ym):[],i={rev:n,flowId:o.id||t,nodes:s,nodeCount:s.length};return typeof o.label=="string"&&(i.label=o.label),typeof o.type=="string"&&(i.type=o.type),i}a(Vo,"getFlowSummary");function Pl(e){let t=[];for(let n of e){if(!n||typeof n!="object"||Array.isArray(n))continue;let r=n;(r.type==="tab"||r.type==="subflow"||Array.isArray(r.nodes))&&t.push(r)}return t}a(Pl,"collectTabs");async function _l(e,t,n){if(!t)throw new x("Missing node id","missing_node_id");if(n){let{rev:d,flow:l}=await Ge(e,n),c=l,u=Array.isArray(c.nodes)?c.nodes:[],p=Array.isArray(c.configs)?c.configs:[],f=[...u,...p].find(h=>h.id===t);if(!f)throw new x("Node was not found","node_not_found",{statusCode:404});return{rev:d,flowId:n,node:f}}let{rev:r,flows:o}=await vt(e),s,i=null;for(let d of o){if(!d||typeof d!="object"||Array.isArray(d))continue;let l=d;if(l.id===t&&typeof l.type=="string"&&l.type!=="tab"&&l.type!=="subflow"){s=l,i=typeof l.z=="string"&&l.z?l.z:null;break}}if(!s)for(let d of Pl(o)){let l=d.id;if(l)try{let u=(await Ge(e,l)).flow,f=[...u.nodes??[],...u.configs??[]].find(h=>h.id===t);if(f){s=f,i=l;break}}catch{}}if(!s)throw new x("Node was not found","node_not_found",{statusCode:404});return{rev:r,flowId:i,node:s}}a(_l,"findNodeInFlows");async function Yo(e,t,n){let r=await _l(e,t,n);return{rev:r.rev,flowId:r.flowId,node:r.node}}a(Yo,"getNodeById");function wm(e){if(e===void 0||e==="")return;let t=/^(\d+)(?:-(\d+))?$/.exec(e.trim());if(!t)throw new x("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");let n=Number(t[1]),r=t[2]!==void 0?Number(t[2]):n;if(!Number.isInteger(n)||!Number.isInteger(r)||n<1)throw new x("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");if(r<n)throw new x("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");return{start:n,end:r}}a(wm,"parseLineRange");async function Zo(e,t,n,r={}){if(!n)throw new x("Missing field name","missing_field");let o=await _l(e,t,r.flowId);if(!(n in o.node))throw new x("Field was not found on the node","field_not_found");let s=o.node[n];if(typeof s!="string")throw new x("Field is not a string","field_not_string");let i=s.split(`
|
|
985
|
+
`),d=i.length,l=Buffer.byteLength(s,"utf8"),c=wm(r.lines);if(!c)return{rev:o.rev,flowId:o.flowId,nodeId:t,field:n,value:s,totalLines:d,bytes:l};if(c.start>d)throw new x(`lines range is out of bounds (start ${c.start} > ${d} total lines)`,"line_range_out_of_bounds",{details:{totalLines:d}});let u=c.start-1,p=Math.min(c.end,i.length),f=i.slice(u,p).join(`
|
|
986
|
+
`);return{rev:o.rev,flowId:o.flowId,nodeId:t,field:n,value:f,totalLines:d,bytes:l,lineRange:{start:c.start,end:Math.min(c.end,d)}}}a(Zo,"getNodeField");function bm(e){let t=[];for(let[n,r]of Object.entries(e))n==="id"||n==="type"||n==="z"||n==="wires"||typeof r=="string"&&r.length>0&&t.push({field:n,value:r});return t}a(bm,"stringFields");function vm(e,t,n){let r=Math.max(0,t-Tl),o=Math.min(e.length,t+n+Tl),s=e.slice(r,o).replace(/\s+/g," ");return r>0&&(s=`\u2026${s}`),o<e.length&&(s=`${s}\u2026`),s}a(vm,"snippetAround");function km(e,t){let n=1;for(let r=0;r<t&&r<e.length;r+=1)e.charCodeAt(r)===10&&(n+=1);return n}a(km,"lineNumberAtIndex");function ji(e,t,n,r,o){for(let{field:s,value:i}of bm(e)){if(r.length>=o)return!0;let d=[],l=new Set,c=-1,u=a((f,h)=>{l.has(f)||(l.add(f),d.push(f)),c<0&&(c=h)},"pushLine");if(!n.includes(`
|
|
987
987
|
`)){let f=i.split(`
|
|
988
|
-
`);for(let h=0;h<f.length;h+=1){let g=(f[h]??"").indexOf(n);if(g>=0){let
|
|
989
|
-
`)){let f=0;for(;f<=i.length;){let h=i.indexOf(n,f);if(h<0)break;u(
|
|
990
|
-
`)}a(hl,"splitLines");Fe();yr();et();Pe();var yl=new Set(["func","template","info","payload","code","text","format","syntax"]),im=new Set(["x","y","w","h","wires","g"]),am=new Set(["w","h","g"]);function nn(e){if(typeof e.name=="string"&&e.name.length>0)return e.name;if(typeof e.label=="string"&&e.label.length>0)return e.label}a(nn,"nodeName");function gl(e){let t=new Map;for(let n of e.nodes??[])t.set(n.id,n);for(let n of e.configs??[])t.set(n.id,n);return t}a(gl,"collectNodes");function es(e){if(e===void 0)return"undefined";if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(es).join(",")}]`;let t=e;return`{${Object.keys(t).sort().map(r=>`${JSON.stringify(r)}:${es(t[r])}`).join(",")}}`}a(es,"stableStringify");function wl(e,t){return es(e)===es(t)}a(wl,"valuesEqual");function ts(e){return typeof e=="string"?e:null}a(ts,"asText");function dm(e){return/<\/?[a-z][\s\S]*>/i.test(e.slice(0,800))}a(dm,"looksLikeHtml");function lm(e,t,n){if(t!=="template")return!1;let r=typeof e.format=="string"?e.format.toLowerCase():"";return r==="html"||r==="handlebars"||r==="mustache"?!0:dm(n)}a(lm,"isHtmlTemplateField");function bl(e,t,n){let r=ts(n);if(yl.has(t)&&r!==null){let o={key:t,kind:"text",value:r};return lm(e,t,r)&&(o.preview="html"),o}return{key:t,kind:"scalar",value:n===void 0?null:n}}a(bl,"toPresenceProperty");function cm(e,t){let n=[];for(let r of[...Object.keys(e)].sort())if(!(am.has(r)||r==="id"||r==="z")){if(r==="wires"){let o=vl(e,t);n.push({key:"wires",kind:"scalar",value:o.length>0?o:e.wires??null});continue}n.push(bl(e,r,e[r]))}return n}a(cm,"nodeProperties");function vl(e,t){let n=e.wires;if(!Array.isArray(n)||n.length===0)return[];let r=[];return n.forEach((o,s)=>{if(!Array.isArray(o))return;let i=[];for(let d of o){if(typeof d!="string"||!d)continue;let l=t.get(d);i.push({id:d,name:l?nn(l)??l.type:d})}r.push({port:s,to:i})}),r}a(vl,"describeWiresStructured");var um=new Set(["func","template","code","text"]);function pm(e,t){let n=[],r=nn(e);r&&n.push({key:"name",kind:"scalar",value:r});let o=vl(e,t);o.length>0&&n.push({key:"wires",kind:"scalar",value:o});for(let s of um){if(!(s in e))continue;let i=ts(e[s]);i===null||i.length===0||n.push(bl(e,s,i))}return n}a(pm,"buildKeyFields");function fm(e,t,n){let r=wt(e.type),o=nn(e),s=o?` "${o}"`:"",i=t?` in "${t}"`:"",d=n===1?"1 field":`${n} fields`;return`Changes ${r} node${s}${i} (${d})`}a(fm,"buildModifiedSummary");function hm(e,t,n){let r=wt(e.type),o=nn(e),s=t==="added"?"Adds":"Removes",i=t==="added"?"to":"from",d=o?` ${JSON.stringify(o)}`:"",l=n?` ${i} flow ${JSON.stringify(n)}`:"",c=`${s} ${r} node${d}${l}`;if(t==="added"&&r==="template"){let u=typeof e.field=="string"&&e.field?e.field:"";u&&(c+=`, output to msg.${u}`)}return c}a(hm,"buildPresenceSummary");function Xo(e,t,n,r){let o={id:e.id,type:e.type,summary:hm(e,t,n),keyFields:pm(e,r),properties:cm(e,r)},s=nn(e);return s!==void 0&&(o.name=s),o}a(Xo,"toPresenceNode");function mm(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let o of[...n].sort()){if(im.has(o)||o==="id"||o==="z")continue;let s=e[o],i=t[o];if(wl(s,i))continue;let d=ts(s),l=ts(i);if(yl.has(o)&&(d!==null||l!==null)){let c=d??"",u=l??"";r.push({key:o,kind:"text",before:c,after:u,lines:ml(c,u)});continue}r.push({key:o,kind:"scalar",before:s===void 0?null:s,after:i===void 0?null:i})}return r}a(mm,"compareNodes");function gm(e,t){let n=["label","disabled","info","env"],r=[];for(let o of n)wl(e[o],t[o])||r.push(o);return r}a(gm,"compareTabMeta");function Zo(e){return typeof e.label=="string"&&e.label.trim()?e.label.trim():""}a(Zo,"flowLabelOf");function Qo(e,t){let n=gl(t),r=gl(e),o=Zo(e)||Zo(t),s=Zo(t)||Zo(e),i=[],d=[],l=[];for(let c of e.nodes??[])n.has(c.id)||i.push(Xo(c,"added",o,r));for(let c of e.configs??[])n.has(c.id)||i.push(Xo(c,"added",o,r));for(let c of t.nodes??[])r.has(c.id)||d.push(Xo(c,"removed",s,n));for(let c of t.configs??[])r.has(c.id)||d.push(Xo(c,"removed",s,n));for(let[c,u]of r){let p=n.get(c);if(!p)continue;let f=mm(p,u);if(f.length>0){let h={id:c,type:u.type||p.type,summary:fm(u,o||s,f.length),changes:f},m=nn(u)??nn(p);m!==void 0&&(h.name=m),l.push(h)}}return l.sort((c,u)=>c.id.localeCompare(u.id)),{summary:{added:i.length,removed:d.length,modified:l.length,tabChanges:gm(t,e),beforeNodeCount:n.size,afterNodeCount:r.size},added:i,removed:d,modified:l}}a(Qo,"buildDiffSections");function xi(e,t,n={}){let r=Qo(e.payload.flow,t.flow),o=n.currentRev??t.rev,s=e.expectedRev,i=n.baseRevStale??(U(e.status)&&s!==o),d={proposalId:e.id,flowId:e.payload.flowId,expectedRev:s,currentRev:o,baseRevStale:i,compareAgainst:n.compareAgainst??"current",...r};return e.warnings&&e.warnings.length>0&&(d.warnings=e.warnings.map(l=>({...l}))),n.baseSnapshotMissing&&(d.baseSnapshotMissing=!0),n.vsCurrent&&(d.vsCurrent=n.vsCurrent),d}a(xi,"buildProposalDiff");async function $n(e,t,n=e){let r=e.payload.flowId,o={id:r,type:"tab",nodes:[],configs:[]},s=e.expectedRev,i=o;try{let u=await t.getFlow(r);s=u.rev,i=u.flow}catch(u){if(!(u instanceof X))throw u;try{s=(await t.getFlows()).rev}catch{}}let d=U(e.status),l=d&&e.expectedRev!==s,c=e.baseFlow;if(d){let u=Oe(t),p=await u.getFlows(),f=oi(n,p),h=u!==t,m=h?lt(n):n,g=Wt(p.flows,r,n.baseFlow)??o,y=h?bt(g).value:g,A=xi(m,{rev:e.expectedRev,flow:m.baseFlow??y},{currentRev:p.rev,compareAgainst:m.baseFlow?"base":"current",baseSnapshotMissing:m.baseFlow===void 0,baseRevStale:e.expectedRev!==p.rev});return A.rebase={state:f.state,conflicts:h?Xs(f.conflicts):f.conflicts,...f.mergedHash?{mergedHash:f.mergedHash}:{}},A.liveChanges=Qo(y,m.baseFlow??y),f.state!=="conflict"&&f.merged&&(A.vsCurrent=Qo(h?bt(f.merged).value:f.merged,y)),A}if(c!==void 0){let u=l?Qo(e.payload.flow,i):void 0;return xi(e,{rev:e.expectedRev,flow:c},{currentRev:s,compareAgainst:"base",baseRevStale:l,...u?{vsCurrent:u}:{}})}return xi(e,{rev:s,flow:i},{compareAgainst:"current",baseSnapshotMissing:!0,baseRevStale:l})}a($n,"computeProposalDiff");Fe();et();Be();ke();var kl=require("path");function ns(e){return(0,kl.join)(e.userDir,".ai-access")}a(ns,"getStorageDir");var ym=["status:read","flows:read","proposals:create","proposals:read-own"];function Ti(){return En()}a(Ti,"readPackageVersion");function wm(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/1e3)):0}a(wm,"secondsRemainingUntil");function bm(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/6e4)):0}a(bm,"minutesRemainingUntil");function vm(e,t){let n=Se(t),r=ht(t),o=`${n}/openapi.yaml`,s=`${n}/openapi.json`;return e==="rest"?{api:n,openapi:o,openapiJson:s,discovery:n}:e==="mcp"?{rpc:r,discovery:n}:{discovery:n}}a(vm,"baseUrlsForTokenKind");function Mn(e,t){let n=Gr(e,t),r=Ee(e.settings);if(t.auth){let o=t.auth.extra?.user;return{ok:!0,version:Ti(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:t.auth.scopes.includes("propose")?"propose":"read",capabilities:{view:!0,manageLinks:!1,approveProposals:!1},defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,maxActiveLinks:r.maxActiveLinks,defaultDeploymentType:r.defaultDeploymentType,caller:{kind:"oauth",clientId:t.auth.clientId,user:typeof o=="string"?o:"owner",scopes:t.auth.scopes}}}if(t.accessLink){let o=Date.now();return{ok:!0,version:Ti(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:null,capabilities:{view:!0,manageLinks:!1,approveProposals:!1},defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,maxActiveLinks:r.maxActiveLinks,defaultDeploymentType:r.defaultDeploymentType,caller:{kind:"access-link",linkId:t.accessLink.id,tokenKind:t.accessLink.kind,...t.accessLink.label!==void 0?{label:t.accessLink.label}:{},expiresAt:t.accessLink.expiresAt,minutesRemaining:bm(t.accessLink.expiresAt,o),secondsRemaining:wm(t.accessLink.expiresAt,o),permissions:[...ym],baseUrls:vm(t.accessLink.kind,e.settings.httpAdminRoot)}}}return{ok:!0,version:Ti(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:n.permission,capabilities:n.capabilities,storageDir:ns(e.settings),defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,maxActiveLinks:r.maxActiveLinks,defaultDeploymentType:r.defaultDeploymentType,caller:{kind:"admin",...n.permission?{permission:n.permission}:{}}}}a(Mn,"buildStatus");function Al(e,t){let n=t.draftStore??new Fo,r=t.rateLimiters??Zd(t.rateLimit??e.settings.aiAccess),o=e.httpAdmin,s="/mcp/a/:token",i=[r.ip,el(e,t.linkStore),r.token];o.use("/mcp/a",Hd()),o.get(s,...i,O((u,p)=>{z(p),p.setHeader("X-Content-Type-Options","nosniff");let f=Se(e.settings.httpAdminRoot),h=u.accessLink;if(!h){j(p,401,"Access link required","access_link_missing");return}let m=Gd({link:h,openapiPath:`${f}/openapi.yaml`,restApiBase:f,mcpRpcPath:ht(e.settings.httpAdminRoot),publicOrigin:vs(u)});p.status(200).type("text/markdown; charset=utf-8").send(m)})),o.get(`${s}/status`,...i,O((u,p)=>{z(p),p.json(Mn(e,u))})),o.get(`${s}/flows`,...i,O(async(u,p)=>{z(p);try{p.json(await ut(t.flowAdapter))}catch(f){let h=W(f);if(h){p.status(h.statusCode).json({...h.body,hint:"Retry after the owner fixes the plugin runtime."});return}j(p,502,f instanceof Error?f.message:String(f),"upstream_error")}})),o.get(`${s}/flows/:id`,...i,O(async(u,p)=>{z(p),await zo(t.flowAdapter,"get")(u,p)})),o.get(`${s}/nodes/:id`,...i,O(async(u,p)=>{z(p),await Wo(t.flowAdapter,"get")(u,p)})),o.get(`${s}/nodes/:id/field/:name`,...i,O(async(u,p)=>{z(p),await Go(t.flowAdapter,"get")(u,p)})),o.get(`${s}/search`,...i,O(async(u,p)=>{z(p),await Ko(t.flowAdapter,"get")(u,p)}));let d={nodeTypes:t.nodeTypesAdapter,...t.catalogueFetcher?{catalogueFetcher:t.catalogueFetcher}:{},...t.catalogueUrl?{catalogueUrl:t.catalogueUrl}:{}};o.get(`${s}/node-types`,...i,O(async(u,p)=>{z(p),await Jo(d)(u,p)})),o.get(`${s}/node-types/:type`,...i,O(async(u,p)=>{z(p),await Vo(d)(u,p)})),o.get(`${s}/palette/search`,...i,O(async(u,p)=>{z(p),await Yo(d)(u,p)}));let l=e.settings.httpAdminRoot;km(o,s,i,t,l),o.get(`${s}/propose`,...i,O(al(t.proposalStore,t.auditLog,t.flowAdapter,l,t.nodeTypesAdapter))),xm(o,s,i,n,t,l);let c=nl();return o.all(`${s}/links`,c),o.all(`${s}/links/:id`,c),o.use("/mcp/a",tl()),n}a(Al,"registerGetLinkRoutes");function km(e,t,n,r,o){e.get(`${t}/proposals`,...n,O(async(s,i)=>{z(i);let d=s.accessLink;if(!d){j(i,401,"Access link required","access_link_missing");return}let l=V(s,o),c=ge(s.query),u=xo(s.query),p=await Qe(r.flowAdapter),f=p?$e(r.proposalStore,p).expired:ne(r.proposalStore),h=Tn(r.proposalStore,d,l,u);i.json({proposals:h.proposals.map(m=>he(r.proposalStore,d,m,{...l,include:c},{includeConflicts:!1})),...Jt({...h,returned:h.proposals.length}),...ue(f)})})),e.get(`${t}/proposals/:id`,...n,O(async(s,i)=>{z(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){j(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){j(i,401,"Access link required","access_link_missing");return}try{let c=V(s,o),u=ge(s.query),p=await Qe(r.flowAdapter),f=ct(r.proposalStore,l,d,c);i.json(he(r.proposalStore,l,f,{...c,include:u},{...p?{live:p}:{}}))}catch(c){let u=W(c,"get");if(u){i.status(u.statusCode).json({...u.body,hint:u.body.hint??"List proposals with GET \u2026/proposals."});return}B(i,c);return}})),Am(e,t,n,r,o),Sm(e,t,n,r,o)}a(km,"registerGetLinkProposalRoutes");function Am(e,t,n,r,o){e.get(`${t}/proposals/:id/withdraw`,...n,O((s,i)=>{z(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){j(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){j(i,401,"Access link required","access_link_missing");return}let c=s.query.confirm;if(c!=="1"&&c!=="true"){j(i,400,"Withdraw over GET requires confirm=1","confirm_required",we("confirm_required","get"));return}let u=typeof s.query.reason=="string"?s.query.reason:void 0,p=V(s,o),f=ge(s.query);try{let h=Rn(r.proposalStore,r.auditLog,l,d,u,p);i.json(he(r.proposalStore,l,h,{...p,include:f}))}catch(h){let m=W(h,"get");if(m){let g=m.body.code;j(i,m.statusCode,nt[g]??"Request failed",g,we(g,"get"));return}j(i,500,"Internal server error","internal_error");return}}))}a(Am,"registerGetLinkWithdrawRoute");function Sm(e,t,n,r,o){e.get(`${t}/proposals/:id/diff`,...n,O(async(s,i)=>{z(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){j(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){j(i,401,"Access link required","access_link_missing");return}try{let c=ct(r.proposalStore,l,d,V(s,o)),u=M(s.query,"format"),p=In(u==="json"||u==="md"?void 0:u);if(!p.ok){j(i,400,"Invalid diff format","invalid_diff_format","Valid formats: summary, full (default summary).");return}i.json(Ln(await $n(c,r.flowAdapter,r.proposalStore.get(d)),p.format))}catch(c){let u=W(c);if(u){i.status(u.statusCode).json(u.body);return}B(i,c);return}}))}a(Sm,"registerGetLinkDiffRoute");function xm(e,t,n,r,o,s){e.get(`${t}/draft/new`,...n,O(ol(r,o.flowAdapter))),e.get(`${t}/draft/:draftId/append`,...n,O(sl(r))),e.get(`${t}/draft/:draftId/submit`,...n,O(il(r,o.proposalStore,o.auditLog,o.flowAdapter,s,o.nodeTypesAdapter)))}a(xm,"registerGetLinkDraftRoutes");se();var Sl=require("module");function Tm(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Sl.createRequire)(e)}a(Tm,"createPluginRequire");function Rm(e){try{let r=Tm()("@node-red/runtime")._?.adminApi?.httpAdmin;if(r&&typeof r.use=="function"&&r!==e.httpAdmin)return r}catch{}return null}a(Rm,"resolveEditorHttpAdmin");function xl(e){let t=[e.httpAdmin],n=Rm(e);return n&&t.push(n),t}a(xl,"httpAdminAppsForMcpErrors");var Tl=Symbol.for("com55.ai-access.mcpJsonErrorHandler");function Pm(e){let t=e??"/";for(;t.endsWith("/");)t=t.slice(0,-1);let n=new Set;for(let r of t?["",t]:[""])for(let o of["/mcp","/mcp/rpc"])n.add(r+o),n.add(`${r+o}/`);return r=>n.has(r)}a(Pm,"mcpRpcMatcher");function Ri(e){return e.includes("/mcp")}a(Ri,"isMcpPath");function _m(e){return e.path||e.url||e.originalUrl||""}a(_m,"requestPath");function Em(e){if(!e||typeof e!="object")return!1;let t=e;if(t.type==="entity.parse.failed"||e instanceof SyntaxError)return!0;let n=typeof e.message=="string"?e.message:"";return/JSON/i.test(n)&&(t.status===400||t.statusCode===400)}a(Em,"isJsonParseError");function Cm(e){if(!e||typeof e!="object")return!1;let t=e;return t.type==="entity.too.large"||t.status===413||t.statusCode===413||t.message==="request entity too large"}a(Cm,"isPayloadTooLarge");function Im(e){let t=Pm(e);return(n,r,o,s)=>{if(o.headersSent){s(n);return}let i=_m(r);if(Em(n)&&Ri(i)){if(t(i)){o.status(400).json({jsonrpc:"2.0",error:{code:-32700,message:"Parse error"},id:null});return}o.status(400).json({error:"Invalid JSON body",code:"invalid_json"});return}if(Cm(n)&&Ri(i)){o.status(413).json({error:`Request body exceeds limit of ${K} bytes`,code:"payload_too_large"});return}if(Ri(i)){if(t(i)){o.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:No(n).error,data:{code:"internal_error"}},id:null});return}o.status(500).json(No(n));return}s(n)}}a(Im,"mcpJsonErrorHandler");function Lm(e,t){let n=e;n[Tl]||(n[Tl]=!0,e.use(Im(t)))}a(Lm,"attachOnce");function Rl(e){for(let t of xl(e))Lm(t,e.settings.httpAdminRoot)}a(Rl,"attachMcpJsonErrorHandlers");xr();var rn=class extends Error{static{a(this,"AccessLinkLimitError")}code="access_link_limit";statusCode=409;constructor(t){super(`Cannot create access link: at most ${t} active links are allowed. Revoke an existing link first.`),this.name="AccessLinkLimitError"}},on=class extends Error{static{a(this,"AccessLinkNotFoundError")}code="access_link_not_found";statusCode=404;constructor(t){super(`Access link '${t}' was not found`),this.name="AccessLinkNotFoundError"}},_r=class extends Error{static{a(this,"AccessLinkValidationError")}code="access_link_invalid";statusCode=400;constructor(t){super(t),this.name="AccessLinkValidationError"}},It=class extends Error{static{a(this,"AccessLinkTtlError")}code="invalid_ttl";statusCode=400;hint=Vt;constructor(t){super(t),this.name="AccessLinkTtlError"}};var sn=require("crypto");xr();function Pi(e){return e==="get_link"||e==="rest"||e==="mcp"}a(Pi,"isAccessLinkKind");function _i(e){return e??"get_link"}a(_i,"resolveAccessLinkKind");function Pl(e){return(0,sn.createHash)("sha256").update(e,"utf8").digest("hex")}a(Pl,"hashToken");function Nm(e,t){let n=Buffer.from(e,"utf8"),r=Buffer.from(t,"utf8");return n.length!==r.length?!1:(0,sn.timingSafeEqual)(n,r)}a(Nm,"hashesEqual");function Er(e){let t={id:e.id,kind:_i(e.kind),createdAt:new Date(e.createdAtMs).toISOString(),expiresAt:new Date(e.expiresAtMs).toISOString()};return e.label!==void 0&&(t.label=e.label),e.lastUsedAtMs!==void 0&&(t.lastUsedAt=new Date(e.lastUsedAtMs).toISOString()),e.createdBy!==void 0&&(t.createdBy=e.createdBy),t}a(Er,"toPublic");var an=class{static{a(this,"AccessLinkStore")}links=new Map;defaultTtlSeconds;maxActiveLinks;now;randomId;randomToken;constructor(t={}){this.defaultTtlSeconds=t.defaultTtlSeconds??14400,this.maxActiveLinks=t.maxActiveLinks??10,this.now=t.now??(()=>Date.now()),this.randomId=t.randomId??(()=>(0,sn.randomBytes)(12).toString("hex")),this.randomToken=t.randomToken??(()=>`aia_${(0,sn.randomBytes)(32).toString("base64url")}`)}configureDefaults(t){typeof t.defaultTtlSeconds=="number"&&Number.isFinite(t.defaultTtlSeconds)&&t.defaultTtlSeconds>0&&(this.defaultTtlSeconds=Math.floor(t.defaultTtlSeconds)),typeof t.maxActiveLinks=="number"&&Number.isFinite(t.maxActiveLinks)&&t.maxActiveLinks>0&&(this.maxActiveLinks=Math.floor(t.maxActiveLinks))}isActive(t,n){return t.revokedAtMs!==void 0?!1:t.expiresAtMs>n}list(){let t=this.now();return[...this.links.values()].filter(n=>this.isActive(n,t)).sort((n,r)=>r.createdAtMs-n.createdAtMs).map(Er)}create(t={}){let n=this.now();if([...this.links.values()].filter(p=>this.isActive(p,n)).length>=this.maxActiveLinks)throw new rn(this.maxActiveLinks);let o=this.defaultTtlSeconds;if(t.ttlSeconds!==void 0){if(typeof t.ttlSeconds!="number"||!Number.isFinite(t.ttlSeconds)||t.ttlSeconds<=0)throw new It("ttlSeconds must be a positive number");o=Math.floor(t.ttlSeconds)}let s=typeof t.label=="string"&&t.label.trim().length>0?t.label.trim():void 0,i=_i(t.kind),d=ui(i);if(o>d)throw new It(pi(i,d));let l=this.randomId(),c=this.randomToken(),u={id:l,tokenHash:Pl(c),kind:i,createdAtMs:n,expiresAtMs:n+o*1e3};return s!==void 0&&(u.label=s),t.createdBy!==void 0&&(u.createdBy=t.createdBy),this.links.set(l,u),{...Er(u),token:c}}revoke(t){let n=this.links.get(t);if(!n)throw new on(t);return n.revokedAtMs??=this.now(),Er(n)}getById(t){if(typeof t!="string"||t.length===0)return null;let n=this.links.get(t);return n?Er(n):null}validateResult(t){if(typeof t!="string"||t.length===0)return{status:"invalid"};let n=Pl(t),r=this.now();for(let o of this.links.values())if(Nm(o.tokenHash,n))return o.revokedAtMs!==void 0?{status:"invalid"}:o.expiresAtMs<=r?{status:"expired",expiresAt:new Date(o.expiresAtMs).toISOString()}:(o.lastUsedAtMs=r,{status:"active",link:Er(o)});return{status:"invalid"}}validate(t){let n=this.validateResult(t);return n.status==="active"?n.link:null}size(){return this.links.size}clear(){this.links.clear()}upsertRecordForTest(t){let n={...t};delete n.token,this.links.set(t.id,n)}};xr();ve();ke();function Ei(e,t){return t}a(Ei,"storeForRequest");function El(e){let t=typeof e.user?.username=="string"?e.user.username:void 0;return t?{kind:"admin",username:t}:{kind:"admin"}}a(El,"adminActor");function rs(e){let t=Ee(e.settings);return new an({defaultTtlSeconds:t.defaultLinkTtlSeconds,maxActiveLinks:t.maxActiveLinks})}a(rs,"createConfiguredLinkStore");function Cl(e){e.setHeader("Cache-Control","no-store"),e.setHeader("Pragma","no-cache")}a(Cl,"noStore");function Il(e,t){return(n,r)=>{let s=Ei(e,t).list();Cl(r),r.json({links:s})}}a(Il,"listLinksHandler");function Ll(e,t,n){return(r,o)=>{let s=r.body??{},i={};if(s.ttlSeconds!==void 0){if(typeof s.ttlSeconds!="number"){o.status(400).json({error:"ttlSeconds must be a positive number",code:"invalid_ttl",hint:Vt});return}i.ttlSeconds=s.ttlSeconds}if(typeof s.label=="string"&&(i.label=s.label),s.kind!==void 0){if(!Pi(s.kind)){o.status(400).json({error:"kind must be one of: 'get_link', 'rest', 'mcp'",code:"invalid_link_kind"});return}i.kind=s.kind}typeof r.user?.username=="string"&&(i.createdBy=r.user.username);try{let d=Ei(e,t).create(i),l=d.kind;if(n){let c=Date.parse(d.createdAt),u=Date.parse(d.expiresAt),p=Number.isFinite(c)&&Number.isFinite(u)&&u>c?Math.round((u-c)/1e3):void 0;n.append("link.created",El(r),{linkId:d.id,label:d.label,expiresAt:d.expiresAt,createdAt:d.createdAt,kind:l,...p!==void 0?{ttlSeconds:p}:{}})}if(Cl(o),l==="get_link"){let c=pa(r,e.settings.httpAdminRoot,d.token);o.status(201).json({...d,url:c});return}o.status(201).json({...d})}catch(d){if(d instanceof rn){o.status(d.statusCode).json({error:d.message,code:d.code});return}if(d instanceof It){o.status(d.statusCode).json({error:d.message,code:d.code,hint:d.hint});return}if(d instanceof _r){o.status(d.statusCode).json({error:d.message,code:d.code});return}B(o,d);return}}}a(Ll,"createLinkHandler");function Nl(e,t,n){return(r,o)=>{let s=r.params.id;if(typeof s!="string"||s.length===0){o.status(400).json({error:"Missing link id"});return}try{let i=Ei(e,t).revoke(s);if(n){let d=Date.parse(i.createdAt),l=Date.parse(i.expiresAt),c=Number.isFinite(d)&&Number.isFinite(l)&&l>d?Math.round((l-d)/1e3):void 0;n.append("link.revoked",El(r),{linkId:i.id,label:i.label,expiresAt:i.expiresAt,createdAt:i.createdAt,kind:i.kind,...c!==void 0?{ttlSeconds:c}:{}})}o.json({revoked:!0,link:i})}catch(i){if(i instanceof on){o.status(i.statusCode).json({error:i.message,code:i.code});return}B(o,i);return}}}a(Nl,"revokeLinkHandler");function Fm(e){let t=Date.parse(e.createdAt),n=Date.parse(e.expiresAt);if(Number.isFinite(t)&&Number.isFinite(n)&&n>t)return Math.round((n-t)/1e3)}a(Fm,"ttlSecondsFromLink");function Om(e,t){if(!t||e.action!=="link.created"&&e.action!=="link.revoked")return e;let n={...e.details??{}},r=typeof n.linkId=="string"?n.linkId:"";if(!r)return e;let o=n.label==null||n.label==="",s=n.expiresAt==null||n.expiresAt==="",i=n.createdAt==null||n.createdAt==="",d=n.ttlSeconds==null;if(!o&&!s&&!i&&!d)return e;let l=t.getById(r);if(!l)return e;if(o&&l.label&&(n.label=l.label),s&&(n.expiresAt=l.expiresAt),i&&(n.createdAt=l.createdAt),d){let c=Fm(l);c!==void 0&&(n.ttlSeconds=c)}return{...e,details:n}}a(Om,"enrichAuditLogEntry");function Fl(e,t){return(n,r)=>{let o=n.query.limit,s=100;if(typeof o=="string"&&o.length>0){let d=Number(o);Number.isFinite(d)&&d>0&&(s=Math.min(Math.floor(d),500))}let i=e.list(s).map(d=>Om(d,t));r.json({logs:i})}}a(Fl,"listLogsHandler");var Ii=require("@modelcontextprotocol/sdk/server/mcp.js"),Bl=require("@modelcontextprotocol/sdk/server/streamableHttp.js"),P=require("zod");Je();se();$t();ie();tt();qt();zt();Fe();et();var Ol=require("async_hooks");Pe();var $m="ai.redacted",$l=new Ol.AsyncLocalStorage;function Ml(e,t,n,r){if(!(r<=0))try{e.append($m,t,{redactions:r,operation:n})}catch{}}a(Ml,"appendRedactionAudit");function Dl(e){let t=$l.getStore();if(!t)return;let n=wo(e);n>0&&t.record(n)}a(Dl,"noteResponseRedactions");function Hl(e,t,n,r){return $l.run({record:a(o=>Ml(e,t,n,o),"record")},r)}a(Hl,"runWithRedactionAudit");function jl(e){let t=Array.isArray(e)?e[0]:e;if(t&&typeof t=="object"){let n=t;if(n.method==="tools/call"&&typeof n.params?.name=="string")return`mcp:${n.params.name.replace(/[^A-Za-z0-9_.-]/g,"").slice(0,64)}`;if(typeof n.method=="string")return`mcp:${n.method.slice(0,64)}`}return"mcp"}a(jl,"mcpOperationName");function Ci(e){let t=e.accessLink;if(t)return{kind:"access-link",linkId:t.id,...t.label!==void 0?{label:t.label}:{}}}a(Ci,"accessLinkAuditActor");function Ul(e){return(t,n,r)=>{if(!t.path.startsWith("/mcp/")||t.path.startsWith("/mcp/rpc")){r();return}let o=n.json.bind(n);n.json=(s=>{let i=Ci(t);if(i&&n.statusCode<400){let d;try{d=String(JSON.stringify(s))}catch{d=""}let l=t.route?.path,c=`${t.method} ${typeof l=="string"?l:"unknown"}`;Ml(e,i,c,wo(d))}return o(s)}),r()}}a(Ul,"redactionAuditMiddleware");Pe();function ql(){return"0.2.0-beta.7"}a(ql,"pluginVersion");function ae(e){let t=JSON.stringify(e);return Dl(t),{content:[{type:"text",text:t}]}}a(ae,"jsonResult");function Li(e){return e===!0?new Set(["payload","baseFlow"]):new Set}a(Li,"includeSetFromPayloadFlag");function de(e,t=500){let n=Sr(e,"mcp");if(n)return{isError:!0,content:[{type:"text",text:JSON.stringify({...n.body,statusCode:n.statusCode})}]};let r=e instanceof Error?e.message:String(e);return{isError:!0,content:[{type:"text",text:JSON.stringify({error:r,code:"internal_error",statusCode:t})}]}}a(de,"jsonError");var Mm=new Set(["list_proposals","list_my_proposals"]),Dm=new Set(["status","limit","offset","mine","flowId"]);function Hm(e){return!!e&&typeof e=="object"&&typeof e.safeParseAsync=="function"}a(Hm,"isZodLikeSchema");function jm(e,t,n){let r=t&&typeof t=="object"?t:{},o=n[0],s=o?.path&&o.path.length>0?String(o.path[0]):"(arguments)";if(Mm.has(e)&&Dm.has(s))return xn(s,r[s]);let i=n.map(l=>({parameter:l.path&&l.path.length>0?l.path.map(String).join("."):"(arguments)",message:l.message??"Invalid value"})),d=i.length===1?`Invalid argument '${i[0]?.parameter}' for tool ${e}: ${i[0]?.message}`:`${i.length} invalid arguments for tool ${e}`;return new b(d,"invalid_arguments",{details:{parameter:s,errors:i,hint:`Fix '${s}' to match the ${e} input schema (see tools/list).`}})}a(jm,"mcpArgumentError");function zl(e){let t=e,n=t.validateToolInput;typeof n=="function"&&(t.validateToolInput=async(r,o,s)=>{let i=r.inputSchema;if(!Hm(i))return n.call(e,r,o,s);let d=await i.safeParseAsync(o??{});if(d.success)return d.data;let l=Sr(jm(s,o,d.error.issues),"mcp"),c=l?{...l.body,statusCode:l.statusCode}:{};throw new Error(JSON.stringify(c))})}a(zl,"installMcpArgumentErrorMapping");function Wl(e,t,n,r){e.registerTool("get_status",{description:'Return plugin status (version, AI feature flags, storage path, deploy defaults). For access-link callers, permission is null and caller.kind is "access-link" \u2014 that is expected, not an error. Use before other tools to confirm the plugin is enabled.'},()=>{try{return ae(Mn(t,r))}catch(o){return de(o)}}),e.registerTool("list_flows",{description:'List all flows as a flat Admin API v2 array plus the current rev string. Likely secrets are shown as "[REDACTED]" (nodes list redacted paths in _aiRedactions). Returns { rev, flows }. Call this (or get_flow) before submit_proposal so expectedRev matches.'},async()=>{try{return ae(await ut(n.flowAdapter))}catch(o){return de(o,502)}}),e.registerTool("get_flow",{description:"Get one tab/subflow by id with nested nodes/configs and the current rev. Pass summary=true for a lightweight node list (id/type/name/wires/bytes, no field contents). Prefer summary \u2192 search_flows \u2192 get_node_field(lines) \u2192 replace_text \u2192 submit_proposal. Returns { rev, flow } or summary shape. Use rev as expectedRev when submitting.",inputSchema:{id:P.z.string().min(1).describe("Flow / tab id"),summary:P.z.boolean().optional().describe("When true, return node list with sizes only (no field contents)")}},async({id:o,summary:s})=>{try{return ae(s?await Do(n.flowAdapter,o):await Me(n.flowAdapter,o))}catch(i){return de(i,502)}}),e.registerTool("get_node",{description:"Get one full node by id (includes owning flowId when known). Optional flow scopes the lookup.",inputSchema:{id:P.z.string().min(1).describe("Node id"),flow:P.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,flow:s})=>{try{return ae(await Ho(n.flowAdapter,o,s))}catch(i){return de(i,502)}}),e.registerTool("get_node_field",{description:'Get one string field from a node. Optional lines="A-B" (1-based inclusive) returns a slice; response always includes totalLines and bytes.',inputSchema:{id:P.z.string().min(1).describe("Node id"),field:P.z.string().min(1).describe("Field name (e.g. template, func)"),lines:P.z.string().optional().describe("Optional 1-based line range A or A-B"),flow:P.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,field:s,lines:i,flow:d})=>{try{return ae(await jo(n.flowAdapter,o,s,{...d?{flowId:d}:{},...i?{lines:i}:{}}))}catch(l){return de(l,502)}}),e.registerTool("search_flows",{description:"Text search across string fields. Returns hits with node id, field, line numbers, and a short snippet. Results are capped; truncated=true when the cap is hit. Optional flow scopes to one tab.",inputSchema:{q:P.z.string().describe("Substring to find (case-sensitive; non-empty required)"),flow:P.z.string().min(1).optional().describe("Optional tab id")}},async({q:o,flow:s})=>{try{return ae(await Uo(n.flowAdapter,o,s?{flowId:s}:{}))}catch(i){return de(i,502)}}),e.registerTool("list_node_types",{description:"List installed Node-RED palette node types (type, module, category, description, \u2026). Optional q filters type/module/category/description (case-insensitive). Results are capped. Call this before proposing nodes with unfamiliar types.",inputSchema:{q:P.z.string().optional().describe("Optional case-insensitive filter")}},async({q:o})=>{try{return ae(await ao(n.nodeTypesAdapter,o?{q:o}:{}))}catch(s){return de(s,502)}}),e.registerTool("get_node_type",{description:"Get detail for one installed node type: defaults (JSON), inputs/outputs (always set), requiredConfigNodeTypes, help sections (summary/inputs/outputs), category/module/color. Types are case-sensitive. Pass includeHelp=false to omit help. 404 node_type_not_found when unknown (didYouMean on case mismatch; else nearest names).",inputSchema:{type:P.z.string().min(1).describe("Node type id (e.g. inject, template) \u2014 case-sensitive"),includeHelp:P.z.boolean().optional().describe("When false, omit help sections (default true)")}},async({type:o,includeHelp:s})=>{try{return ae(await Bt(n.nodeTypesAdapter,o,{includeHelp:s!==!1}))}catch(i){return de(i,502)}}),e.registerTool("search_palette",{description:"Search the public Node-RED catalogue for modules to recommend. Returns module id, description, version, updated, link, and installed boolean (cross-checked against installed modules). Core node-red types are not in the catalogue \u2014 use list_node_types. RECOMMEND ONLY \u2014 never installs.",inputSchema:{q:P.z.string().describe("Search string (id / description / keywords; non-empty required)")}},async({q:o})=>{try{return ae(await lo(n.nodeTypesAdapter,o,{...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{}}))}catch(s){return de(s,502)}})}a(Wl,"registerReadTools");function Gl(e,t,n,r,o){let s=V(r,t.settings.httpAdminRoot);e.registerTool("submit_proposal",{description:`Submit a replace_flow proposal for owner approval (does not deploy). Preferred for small string edits: payload: { flowId, nodes: [{ op:"replace_text", node, field, find, replace, occurrence? }] } \u2014 find must be unique unless occurrence (1-based) is set; multiple replace_text ops apply in order; yields a short readable diff. Or send a full tab: payload.flow from get_flow (id, type, label?, nodes[], configs?). Inputs (all top-level, next to each other): type="replace_flow", expectedRev (required string from list_flows/get_flow rev; expected_rev alias accepted), optional title (\u226480 chars), and payload: { flowId, flow? } and/or payload.nodes / top-level nodes. Do not nest expectedRev inside payload. Secret values appear as "[REDACTED]" in reads; leave the placeholder unchanged to keep the stored secret (it is restored on submit), or replace it with a new value to change it. Payload size limit is ${K} bytes (413 if exceeded). Recent known expectedRev snapshots are accepted and auto-rebased; unknown revisions return 409 version_mismatch. Open proposals auto-rebase on revision changes; conflict blocks approval until resolved. Proposals expire after the configured TTL (default 7 days). replace_text errors: 400 text_not_found; 409 ambiguous_match with matchCount. Unknown palette types return 400 unknown_node_type with nearest matches \u2014 call list_node_types / search_palette and ask the user to install via Manage palette (never auto-install). Returns a lean proposal (id, status, title, storedTitle, flowLabel, displayName, reviewUrl, changeSummary, \u2026) by default; title may gain " in <flow>"; storedTitle is the title as saved (your title without that suffix, or the auto-generated title, which may already name the flow). Pass include_payload=true to include payload and baseFlow. When telling the user about the proposal, cite displayName and reviewUrl \u2014 not the raw id. Humans approve on the /ai-access dashboard (reviewUrl opens/scrolls to it).`,inputSchema:{type:P.z.literal("replace_flow").describe("Must be 'replace_flow'"),expectedRev:P.z.string().min(1).optional().describe("Top-level current flow revision from get_flow/list_flows (required at submit; sit next to type and payload, not inside payload)"),expected_rev:P.z.string().min(1).optional().describe("Alias for expectedRev (snake_case); prefer expectedRev"),title:P.z.string().max(80).optional().describe("Optional short human title (\u226480 chars). Auto-generated from changes when omitted."),include_payload:P.z.boolean().optional().describe("When true, include payload and baseFlow in the response (default lean)"),nodes:P.z.array(P.z.record(P.z.string(),P.z.unknown())).optional().describe("Optional node ops (set/add/remove/replace_text). Same as payload.nodes when flow is omitted."),payload:P.z.object({flowId:P.z.string().min(1).describe("Tab id to replace"),flow:P.z.object({id:P.z.string().optional(),type:P.z.string().optional().describe("Usually 'tab' (included by get_flow)"),label:P.z.string().optional(),nodes:P.z.array(P.z.record(P.z.string(),P.z.unknown())).optional().describe("Child nodes for the tab (from get_flow.nodes)"),configs:P.z.array(P.z.record(P.z.string(),P.z.unknown())).optional()}).passthrough().optional().describe("Full tab from get_flow; omit when using nodes ops"),nodes:P.z.array(P.z.record(P.z.string(),P.z.unknown())).optional().describe("Node ops applied to the live tab (prefer replace_text for small string edits)")}).describe("Replacement flow payload and/or node ops")}},async i=>{try{let d=await Tt(n.proposalStore,n.auditLog,o,{...i,expectedRev:typeof i.expectedRev=="string"&&i.expectedRev.length>0?i.expectedRev:i.expected_rev},n.flowAdapter,s,n.nodeTypesAdapter),l={...fe(d,{...s,include:Li(i.include_payload)}),mine:!0},c=ne(n.proposalStore),u=Ye(Ve(n.proposalStore,n.proposalStore.get(d.id),o.id));return ae({...l,...u,...ue(c)})}catch(d){return de(d)}}),Bm(e,n,o,s),qm(e,n,o,s)}a(Gl,"registerProposalTools");function Um(e,t,n,r){let o={mine:P.z.boolean().optional().describe("true: only proposals created by this access link; false: only other links\u2019 proposals"),status:P.z.union([P.z.string(),P.z.array(P.z.string())]).optional().describe("Filter by proposal status: one value, an array, or a comma-separated string (pending, conflict, approved, rejected, failed, withdrawn, expired, superseded). An empty array or empty string means no status filter."),flowId:P.z.string().optional().describe("Filter by flow tab id (payload.flowId)"),limit:P.z.number().int().min(1).max(Sn).optional().describe(`Page size (1\u2013${Sn}). Omit to return all matches.`),offset:P.z.number().int().min(0).optional().describe("Skip this many filtered results (default 0)"),include_payload:P.z.boolean().optional().describe("When true, include payload and baseFlow on each proposal")},s=a(async i=>{try{let d=Li(i.include_payload),l=await Qe(t.flowAdapter),c=l?$e(t.proposalStore,l).expired:ne(t.proposalStore),u={};typeof i.mine=="boolean"&&(u.mine=i.mine);let p=ci(i.status);p&&(u.status=p),typeof i.flowId=="string"&&i.flowId.length>0&&(u.flowId=i.flowId),typeof i.limit=="number"&&(u.limit=i.limit),typeof i.offset=="number"&&(u.offset=i.offset);let f=Tn(t.proposalStore,n,r,u);return ae({proposals:f.proposals.map(h=>he(t.proposalStore,n,h,{...r,include:d},{includeConflicts:!1})),...Jt({...f,returned:f.proposals.length}),...ue(c)})}catch(d){return de(d)}},"listProposalsHandler");e.registerTool("list_proposals",{description:"List proposals visible to this access link (all proposals by default). Each item includes mine:true|false for the caller. Filter with mine, status, and/or flowId; paginate with limit/offset (response includes total, offset, hasMore, nextOffset). Invalid filters return invalid_filter. Returns lean { proposals, total, \u2026 } with title, displayName, reviewUrl, changeSummary (no payload/baseFlow unless include_payload=true). Prefer displayName + reviewUrl when referring to a proposal with the user (not the raw id).",inputSchema:o},s),e.registerTool("list_my_proposals",{description:"Alias of list_proposals. Prefer list_proposals. Lists proposals visible to this access link with optional mine/status/flowId filters and limit/offset pagination.",inputSchema:o},s)}a(Um,"registerListProposalsTools");function Bm(e,t,n,r){Um(e,t,n,r),e.registerTool("get_proposal",{description:"Get any proposal by id (all tokens can read). Response includes mine:true|false and, for open proposals (pending or conflict), conflictingProposals when other open work touches the same flow. Lean by default (no payload/baseFlow); pass include_payload=true for full bodies. Cite displayName + reviewUrl when talking to the user.",inputSchema:{id:P.z.string().min(1).describe("Proposal id (prop-\u2026)"),include_payload:P.z.boolean().optional().describe("When true, include payload and baseFlow")}},async({id:o,include_payload:s})=>{try{let i=await Qe(t.flowAdapter),d=ct(t.proposalStore,n,o,r);return ae(he(t.proposalStore,n,d,{...r,include:Li(s)},{...i?{live:i}:{}}))}catch(i){return de(i)}})}a(Bm,"registerProposalQueryTools");function qm(e,t,n,r){e.registerTool("get_proposal_diff",{description:"Diff any proposal against its stored baseFlow snapshot (what changed). Optional format=summary|full (default summary). Open proposals include rebase and liveChanges; conflict blocks approval, otherwise vsCurrent shows the merged deployment.",inputSchema:{id:P.z.string().min(1).describe("Proposal id (prop-\u2026)"),format:P.z.enum(["summary","full"]).optional().describe("Diff payload format (default summary)")}},async({id:o,format:s})=>{try{let i=ct(t.proposalStore,n,o),d=In(s??"summary");if(!d.ok)return de(new b("Invalid diff format","invalid_diff_format",{details:{hint:"Valid formats: summary, full (default summary)."}}));let l=Ln(await $n(i,t.flowAdapter,t.proposalStore.get(o)),d.format);return ae(l)}catch(i){return de(i)}}),e.registerTool("resolve_proposal_conflict",{description:"Replace your own open proposal with a resolution based on expectedRev. Revision changes auto-rebase; conflict blocks approval until resolved. Open proposals expire after the configured TTL (default 7 days). Read the current flow first; payload accepts a full flow or nodes ops as in submit_proposal.",inputSchema:{id:P.z.string().min(1),expectedRev:P.z.string().min(1),title:P.z.string().max(80).optional(),payload:P.z.object({flowId:P.z.string().min(1),flow:P.z.record(P.z.string(),P.z.unknown()).optional(),nodes:P.z.array(P.z.record(P.z.string(),P.z.unknown())).optional()})}},async({id:o,...s})=>{try{let i=await To(t.proposalStore,t.auditLog,n,o,s,t.flowAdapter,r,t.nodeTypesAdapter);return ae(he(t.proposalStore,n,i,r))}catch(i){return de(i)}}),e.registerTool("withdraw_proposal",{description:"Withdraw one of YOUR open (pending or conflict) proposals (status becomes withdrawn, not rejected). Optional reason. Only the creating access link can withdraw; other tokens get 403. Cite displayName + reviewUrl when telling the user.",inputSchema:{id:P.z.string().min(1).describe("Proposal id (prop-\u2026) owned by this access link"),reason:P.z.string().optional().describe("Optional withdraw reason")}},({id:o,reason:s})=>{try{let i=Rn(t.proposalStore,t.auditLog,n,o,s,r);return ae(he(t.proposalStore,n,i,r))}catch(i){return de(i)}})}a(qm,"registerProposalActionTools");function zm(e,t,n){let r=new Ii.McpServer({name:"node-red-ai-access",version:ql()});zl(r);let o=n.accessLink;if(!o)throw new Error("Access link required");return Wl(r,e,t,n),Gl(r,e,t,n,o),r}a(zm,"createAiMcpServer");function Wm(e){let t=Ci(e);if(t)return t;let n=e.auth?.clientId??"",r=typeof e.auth?.extra?.user=="string"?e.auth.extra.user:"owner";return{kind:"access-link",linkId:to(n,r),label:"oauth"}}a(Wm,"mcpAuditActor");function Ni(e,t){return async(n,r,o)=>{try{if(!n.accessLink&&!n.auth){r.setHeader("WWW-Authenticate",ce),r.status(401).json({error:xe,code:qe});return}let s={...t,flowAdapter:kt(t.flowAdapter)},i=n.accessLink?zm(e,s,n):Gm(e,s,n),d=new Bl.StreamableHTTPServerTransport({enableJsonResponse:!0});await i.connect(d),await Hl(t.auditLog,Wm(n),jl(n.body),()=>d.handleRequest(n,r,n.body)),r.on("close",()=>{d.close(),i.close()})}catch(s){if(!r.headersSent){B(r,s);return}o(s instanceof Error?s:new Error(String(s)))}}}a(Ni,"mcpRpcPostHandler");function Gm(e,t,n){let r=new Ii.McpServer({name:"node-red-ai-access",version:ql()});if(zl(r),Wl(r,e,t,n),(n.auth?.scopes??[]).includes("propose")){let s=typeof n.auth?.extra?.user=="string"?n.auth.extra.user:"owner",i=n.auth?.clientId??"",l={id:to(i,s),kind:"mcp",createdAt:new Date().toISOString(),expiresAt:new Date((n.auth?.expiresAt??0)*1e3).toISOString(),createdBy:s,oauthClientId:i,oauthUser:s};Gl(r,e,t,n,l)}return r}a(Gm,"createOAuthMcpServer");function Cr(e,t){t.status(405).set("Allow","POST").json({jsonrpc:"2.0",error:{code:-32e3,message:"Method not allowed. Use POST for Streamable HTTP."},id:null})}a(Cr,"mcpRpcMethodNotAllowed");var Ir=require("crypto"),Hn=require("fs"),Mi=require("path"),Q=require("@modelcontextprotocol/sdk/server/auth/errors.js"),Xl=require("@modelcontextprotocol/sdk/server/auth/handlers/authorize.js"),Di=require("@modelcontextprotocol/sdk/server/auth/handlers/metadata.js"),Zl=require("@modelcontextprotocol/sdk/server/auth/handlers/register.js"),Ql=require("@modelcontextprotocol/sdk/server/auth/handlers/revoke.js"),ec=require("@modelcontextprotocol/sdk/server/auth/handlers/token.js"),tc=require("@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js"),nc=require("@modelcontextprotocol/sdk/server/auth/router.js"),rc=zn(require("express"),1);se();Be();function rt(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t]??t)}a(rt,"esc");var Kl={read:'<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5c-5 0-9 4.5-10 7 1 2.5 5 7 10 7s9-4.5 10-7c-1-2.5-5-7-10-7Zm0 11.5a4.5 4.5 0 1 1 0-9 4.5 4.5 0 0 1 0 9Zm0-2.3a2.2 2.2 0 1 0 0-4.4 2.2 2.2 0 0 0 0 4.4Z"/></svg>',propose:'<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 17.2V20h2.8l8.3-8.3-2.8-2.8L4 17.2Zm13.7-7.6a1 1 0 0 0 0-1.4l-1.9-1.9a1 1 0 0 0-1.4 0l-1.4 1.4 2.8 2.8 1.9-1.9Z"/></svg>'},Km={read:["Read your flows","View flows, nodes, node types and AI Access logs."],propose:["Propose flow changes","Submit change proposals. Nothing is deployed until you approve it in Node-RED."]},Fi='<svg class="logo" viewBox="0 0 48 48" aria-hidden="true"><rect width="48" height="48" rx="11" fill="#8f0000"/><path d="M11 15h9a4 4 0 0 1 4 4v10a4 4 0 0 0 4 4h9" fill="none" stroke="#fff" stroke-width="3" stroke-linecap="round"/><rect x="6" y="11" width="9" height="8" rx="2.5" fill="#fff"/><rect x="33" y="29" width="9" height="8" rx="2.5" fill="#fff"/><circle cx="37.5" cy="16" r="5" fill="none" stroke="#fff" stroke-width="2.4"/><path d="M37.5 13.5v5M35 16h5" stroke="#fff" stroke-width="2.2" stroke-linecap="round"/></svg>',Jm=`
|
|
988
|
+
`);for(let h=0;h<f.length;h+=1){let g=(f[h]??"").indexOf(n);if(g>=0){let w=0;for(let v=0;v<h;v+=1)w+=(f[v]?.length??0)+1;u(h+1,w+g)}}}if(n.includes(`
|
|
989
|
+
`)){let f=0;for(;f<=i.length;){let h=i.indexOf(n,f);if(h<0)break;u(km(i,h),h),f=h+Math.max(1,n.length)}}if(d.length===0)continue;let p=d.length;r.push({flowId:t,nodeId:e.id,field:s,lines:d.slice(0,mm),lineCount:p,snippet:vm(i,Math.max(0,c),n.length)})}return r.length>=o}a(ji,"searchInNode");function Cl(e){return[...e.nodes??[],...e.configs??[]]}a(Cl,"nodePool");async function Am(e,t,n,r,o){let s=await Ge(e,t);for(let i of Cl(s.flow))if(ji(i,t,n,r,o))return{rev:s.rev,truncated:!0};return{rev:s.rev,truncated:!1}}a(Am,"searchOneFlow");async function xm(e,t,n,r){let o=await vt(e),s=new Set;for(let i of Pl(o.flows))if(i.id)try{let d=await Ge(e,i.id);for(let l of Cl(d.flow))if(s.add(l.id),ji(l,i.id,t,n,r))return{rev:o.rev,truncated:!0}}catch{}for(let i of o.flows){if(!i||typeof i!="object"||Array.isArray(i))continue;let d=i;if(!d.id||s.has(d.id)||d.type==="tab"||d.type==="subflow")continue;let l=typeof d.z=="string"?d.z:null;if(ji(d,l,t,n,r))return{rev:o.rev,truncated:!0}}return{rev:o.rev,truncated:!1}}a(xm,"searchAllFlows");async function Xo(e,t,n={}){if(typeof t!="string"||!t.trim())throw new x("q query parameter is required","missing_query");let r=typeof n.cap=="number"&&n.cap>0?Math.floor(n.cap):hm,o=[],s=n.flowId?await Am(e,n.flowId,t,o,r):await xm(e,t,o,r),i={rev:s.rev,q:t,hits:o,hitCount:o.length,truncated:s.truncated,cap:r};return n.flowId&&(i.flowId=n.flowId),i}a(Xo,"searchFlows");function El(e){if(e===void 0)return!1;let t=e.toLowerCase();return t==="1"||t==="true"||t==="yes"}a(El,"isTruthyQueryFlag");Ne();function Sm(e,t,n){switch(e){case"node_not_found":case"flow_not_found":case"field_not_found":return n??Ae(e,t);default:return Ae(e,t)}}a(Sm,"layeredReadErrorHint");function un(e){return ut[e]??"Request failed"}a(un,"safeErrorText");function Oe(e,t,n){e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("Content-Type","application/json; charset=utf-8"),e.status(t).json(n)}a(Oe,"sendJson");function Qo(e,t,n,r){let o=J(t,n);if(!o)return!1;let s=o.body.code,i={error:un(s),code:s};typeof o.body.matchCount=="number"&&(i.matchCount=o.body.matchCount),typeof o.body.totalLines=="number"&&(i.totalLines=o.body.totalLines),typeof o.body.expectedRev=="string"&&(i.expectedRev=o.body.expectedRev),typeof o.body.currentRev=="string"&&(i.currentRev=o.body.currentRev);let d=Sm(s,n,r);return d&&(i.hint=d),Oe(e,o.statusCode,i),!0}a(Qo,"sendMappedError");function es(e){Oe(e,502,{error:un("upstream_error"),code:"upstream_error"})}a(es,"sendUpstreamError");function ts(e,t="rest"){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){Oe(r,400,{error:un("missing_flow_id"),code:"missing_flow_id"});return}let s=El(j(n.query,"summary"));try{if(s){Oe(r,200,await Vo(rt(n,e),o));return}Oe(r,200,await Ge(rt(n,e),o))}catch(i){if(Qo(r,i,t))return;es(r)}}}a(ts,"getFlowOrSummaryHandler");function ns(e,t="rest"){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||!o){Oe(r,400,{error:un("missing_node_id"),code:"missing_node_id"});return}let s=j(n.query,"flow");try{Oe(r,200,await Yo(rt(n,e),o,s))}catch(i){if(Qo(r,i,t,t==="get"?"Call GET \u2026/flows/<id>?summary=1 or \u2026/search?q=\u2026":t==="mcp"?void 0:"Call GET \u2026/flows/<id>?summary=1 or \u2026/search?q=\u2026"))return;es(r)}}}a(ns,"getNodeHandler");function rs(e,t="rest"){return async(n,r)=>{let o=n.params.id,s=n.params.name;if(typeof o!="string"||!o){Oe(r,400,{error:un("missing_node_id"),code:"missing_node_id"});return}if(typeof s!="string"||!s){Oe(r,400,{error:un("missing_field"),code:"missing_field"});return}let i=j(n.query,"flow"),d=j(n.query,"lines");try{Oe(r,200,await Zo(rt(n,e),o,s,{...i?{flowId:i}:{},...d?{lines:d}:{}}))}catch(l){if(Qo(r,l,t,"Use summary/search to discover node ids and fields."))return;es(r)}}}a(rs,"getNodeFieldHandler");function os(e,t="rest"){return async(n,r)=>{let o=j(n.query,"q");if(!o){Oe(r,400,{error:un("missing_query"),code:"missing_query",hint:t==="get"?"Example: GET \u2026/search?q=TODO&flow=tab1":Ae("missing_query",t)});return}let s=j(n.query,"flow");try{Oe(r,200,await Xo(rt(n,e),o,s?{flowId:s}:{}))}catch(i){if(Qo(r,i,t))return;es(r)}}}a(os,"searchFlowsHandler");y();le();Zt();var Rm={missing_node_type:"Missing node type",missing_query:"q query parameter is required",node_type_not_found:"Node type was not found",catalogue_unavailable:"Node-RED catalogue unavailable",unknown_node_type:"Unknown node type",upstream_error:"Upstream error"};function $r(e){return Rm[e]??"Request failed"}a($r,"safeErrorText");function Ht(e,t,n){e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("Content-Type","application/json; charset=utf-8"),e.setHeader("Cache-Control","no-store"),e.status(t).json(n)}a(Ht,"sendJson");function Ui(e,t){let n=J(t);if(!n)return!1;let r=n.body.code,o={error:$r(r),code:r};return typeof n.body.type=="string"&&(o.type=n.body.type),Array.isArray(n.body.nearest)&&(o.nearest=n.body.nearest),typeof n.body.didYouMean=="string"?(o.didYouMean=n.body.didYouMean,o.caseSensitive=!0,o.hint=`Types are case-sensitive \u2014 use '${n.body.didYouMean}' exactly.`):(r==="node_type_not_found"||r==="unknown_node_type")&&(o.hint="Call GET \u2026/node-types?q= or \u2026/palette/search?q=. Ask the user to install via Manage palette, then recheck node-types. This API never installs."),r==="catalogue_unavailable"&&(o.hint="Retry later; palette search is recommend-only and never installs packages."),Ht(e,n.statusCode,o),!0}a(Ui,"sendMappedError");function ss(e){return async(t,n)=>{try{let r=j(t.query,"q"),o=await bo(e.nodeTypes,r?{q:r}:{});Ht(n,200,o)}catch(r){if(Ui(n,r))return;Ht(n,502,{error:$r("upstream_error"),code:"upstream_error"})}}}a(ss,"listNodeTypesHandler");function is(e){return async(t,n)=>{try{let r=t.params.type,o=typeof r=="string"?decodeURIComponent(r):"",s=j(t.query,"help"),i=!(s==="0"||s==="false"||s==="no"||s==="omit"),d=await Yt(e.nodeTypes,o,{includeHelp:i});Ht(n,200,d)}catch(r){if(Ui(n,r))return;Ht(n,502,{error:$r("upstream_error"),code:"upstream_error"})}}}a(is,"getNodeTypeHandler");function as(e){return async(t,n)=>{try{let r=j(t.query,"q");if(!r){Ht(n,400,{error:$r("missing_query"),code:"missing_query",hint:"Pass ?q= to search the Node-RED catalogue (recommend only)."});return}let o=await vo(e.nodeTypes,r,{...e.catalogueFetcher?{catalogueFetcher:e.catalogueFetcher}:{},...e.catalogueUrl?{catalogueUrl:e.catalogueUrl}:{}});Ht(n,200,o)}catch(r){if(Ui(n,r))return;Ht(n,502,{error:$r("upstream_error"),code:"upstream_error"})}}}a(as,"searchPaletteHandler");en();y();Ee();ao();tt();y();function Vn(e,t){return e[t]??""}a(Vn,"lineAt");function Mr(e,t,n){let r=e[t];return r?r[n]??0:0}a(Mr,"cell");function Il(e,t,n,r){let o=e[t];o&&(o[n]=r)}a(Il,"setCell");function Nl(e,t){let n=Ll(e),r=Ll(t),o=n.length,s=r.length,i=[];for(let u=0;u<=o;u+=1){let p=[];for(let f=0;f<=s;f+=1)p.push(0);i.push(p)}for(let u=o-1;u>=0;u-=1)for(let p=s-1;p>=0;p-=1)Vn(n,u)===Vn(r,p)?Il(i,u,p,Mr(i,u+1,p+1)+1):Il(i,u,p,Math.max(Mr(i,u+1,p),Mr(i,u,p+1)));let d=[],l=0,c=0;for(;l<o&&c<s;){let u=Vn(n,l),p=Vn(r,c);u===p?(d.push({op:"equal",text:u}),l+=1,c+=1):Mr(i,l+1,c)>=Mr(i,l,c+1)?(d.push({op:"remove",text:u}),l+=1):(d.push({op:"add",text:p}),c+=1)}for(;l<o;)d.push({op:"remove",text:Vn(n,l)}),l+=1;for(;c<s;)d.push({op:"add",text:Vn(r,c)}),c+=1;return d}a(Nl,"diffLines");function Ll(e){return e.length===0?[]:e.split(`
|
|
990
|
+
`)}a(Ll,"splitLines");qe();On();lt();Ne();ao();var $l=new Set(["func","template","info","payload","code","text","format","syntax"]),Tm=new Set(["x","y","w","h","wires","g"]),Pm=new Set(["w","h","g"]);function pn(e){if(typeof e.name=="string"&&e.name.length>0)return e.name;if(typeof e.label=="string"&&e.label.length>0)return e.label}a(pn,"nodeName");function Fl(e){let t=new Map;for(let n of e.nodes??[])t.set(n.id,n);for(let n of e.configs??[])t.set(n.id,n);return t}a(Fl,"collectNodes");function us(e){if(e===void 0)return"undefined";if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(us).join(",")}]`;let t=e;return`{${Object.keys(t).sort().map(r=>`${JSON.stringify(r)}:${us(t[r])}`).join(",")}}`}a(us,"stableStringify");function Ml(e,t){return us(e)===us(t)}a(Ml,"valuesEqual");function ps(e){return typeof e=="string"?e:null}a(ps,"asText");function _m(e){return/<\/?[a-z][\s\S]*>/i.test(e.slice(0,800))}a(_m,"looksLikeHtml");function Cm(e,t,n){if(t!=="template")return!1;let r=typeof e.format=="string"?e.format.toLowerCase():"";return r==="html"||r==="handlebars"||r==="mustache"?!0:_m(n)}a(Cm,"isHtmlTemplateField");function Dl(e,t,n){let r=ps(n);if($l.has(t)&&r!==null){let o={key:t,kind:"text",value:r};return Cm(e,t,r)&&(o.preview="html"),o}return{key:t,kind:"scalar",value:n===void 0?null:n}}a(Dl,"toPresenceProperty");function Em(e,t){let n=[];for(let r of[...Object.keys(e)].sort())if(!(Pm.has(r)||r==="id"||r==="z")){if(r==="wires"){let o=Hl(e,t);n.push({key:"wires",kind:"scalar",value:o.length>0?o:e.wires??null});continue}n.push(Dl(e,r,e[r]))}return n}a(Em,"nodeProperties");function Hl(e,t){let n=e.wires;if(!Array.isArray(n)||n.length===0)return[];let r=[];return n.forEach((o,s)=>{if(!Array.isArray(o))return;let i=[];for(let d of o){if(typeof d!="string"||!d)continue;let l=t.get(d);i.push({id:d,name:l?pn(l)??l.type:d})}r.push({port:s,to:i})}),r}a(Hl,"describeWiresStructured");var Im=new Set(["func","template","code","text"]);function Lm(e,t){let n=[],r=pn(e);r&&n.push({key:"name",kind:"scalar",value:r});let o=Hl(e,t);o.length>0&&n.push({key:"wires",kind:"scalar",value:o});for(let s of Im){if(!(s in e))continue;let i=ps(e[s]);i===null||i.length===0||n.push(Dl(e,s,i))}return n}a(Lm,"buildKeyFields");function Nm(e,t,n){let r=Et(e.type),o=pn(e),s=o?` "${o}"`:"",i=t?` in "${t}"`:"",d=n===1?"1 field":`${n} fields`;return`Changes ${r} node${s}${i} (${d})`}a(Nm,"buildModifiedSummary");function Fm(e,t,n){let r=Et(e.type),o=pn(e),s=t==="added"?"Adds":"Removes",i=t==="added"?"to":"from",d=o?` ${JSON.stringify(o)}`:"",l=n?` ${i} flow ${JSON.stringify(n)}`:"",c=`${s} ${r} node${d}${l}`;if(t==="added"&&r==="template"){let u=typeof e.field=="string"&&e.field?e.field:"";u&&(c+=`, output to msg.${u}`)}return c}a(Fm,"buildPresenceSummary");function ds(e,t,n,r){let o={id:e.id,type:e.type,summary:Fm(e,t,n),keyFields:Lm(e,r),properties:Em(e,r)},s=pn(e);return s!==void 0&&(o.name=s),o}a(ds,"toPresenceNode");function Om(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let o of[...n].sort()){if(Tm.has(o)||o==="id"||o==="z")continue;let s=e[o],i=t[o];if(Ml(s,i))continue;let d=ps(s),l=ps(i);if($l.has(o)&&(d!==null||l!==null)){let c=d??"",u=l??"";r.push({key:o,kind:"text",before:c,after:u,lines:Nl(c,u)});continue}r.push({key:o,kind:"scalar",before:s===void 0?null:s,after:i===void 0?null:i})}return r}a(Om,"compareNodes");function Ol(e,t){let n=["label","disabled","info","env"],r=[];for(let o of n){let s=o==="disabled"?!1:o==="env"?[]:"";Ml(e[o]??s,t[o]??s)||r.push(o)}return r}a(Ol,"compareTabMeta");function ls(e){return typeof e.label=="string"&&e.label.trim()?e.label.trim():""}a(ls,"flowLabelOf");function cs(e,t){let n=Fl(t),r=Fl(e),o=ls(e)||ls(t),s=ls(t)||ls(e),i=[],d=[],l=[];for(let c of e.nodes??[])n.has(c.id)||i.push(ds(c,"added",o,r));for(let c of e.configs??[])n.has(c.id)||i.push(ds(c,"added",o,r));for(let c of t.nodes??[])r.has(c.id)||d.push(ds(c,"removed",s,n));for(let c of t.configs??[])r.has(c.id)||d.push(ds(c,"removed",s,n));for(let[c,u]of r){let p=n.get(c);if(!p)continue;let f=Om(p,u);if(f.length>0){let h={id:c,type:u.type||p.type,summary:Nm(u,o||s,f.length),changes:f},m=pn(u)??pn(p);m!==void 0&&(h.name=m),l.push(h)}}return l.sort((c,u)=>c.id.localeCompare(u.id)),{summary:{added:i.length,removed:d.length,modified:l.length,tabChanges:Ol(t,e),beforeNodeCount:n.size,afterNodeCount:r.size},tabProperties:Ol(t,e).map(c=>({key:c,kind:"scalar",before:t[c],after:e[c]})),added:i,removed:d,modified:l}}a(cs,"buildDiffSections");function Bi(e,t,n={}){let r=cs(e.payload.flow,t.flow),o=n.currentRev??t.rev,s=e.expectedRev,i=n.baseRevChanged??(U(e.status)&&s!==o),d={proposalId:e.id,flowId:e.payload.flowId,expectedRev:s,baseRev:s,currentRev:o,baseRevChanged:i,compareAgainst:n.compareAgainst??"current",...r};return e.warnings&&e.warnings.length>0&&(d.warnings=e.warnings.map(l=>({...l}))),n.baseSnapshotMissing&&(d.baseSnapshotMissing=!0),n.vsCurrent&&(d.vsCurrent=n.vsCurrent),d}a(Bi,"buildProposalDiff");async function Yn(e,t,n=e){let r=e.payload.flowId,o={id:r,type:"tab",nodes:[],configs:[]},s=e.expectedRev,i=o;try{let u=await t.getFlow(r);s=u.rev,i=u.flow}catch(u){if(!(u instanceof te))throw u;try{s=(await t.getFlows()).rev}catch{}}let d=U(e.status),l=d&&e.expectedRev!==s,c=e.baseFlow;if(d){let u=we(t),p=await u.getFlows(),f=bi(n,p),h=u!==t,m=h?nt(n):n,g=st(p.flows,r,n.baseFlow)??o,w=h?yt(g).value:g,v=Bi(m,{rev:e.expectedRev,flow:m.baseFlow??w},{currentRev:p.rev,compareAgainst:m.baseFlow?"base":"current",baseSnapshotMissing:m.baseFlow===void 0,baseRevChanged:e.expectedRev!==p.rev});v.rebase={rev:f.rev,state:f.state,conflicts:h?pi(f.conflicts):f.conflicts,...f.mergedHash?{mergedHash:f.mergedHash}:{}};let k=a((C,O)=>O===r?C:[...C?.nodes??[],...C?.configs??[]].find(q=>q.id===O),"target");v.conflictValues=f.conflicts.map(C=>({nodeId:C.nodeId??"",fields:(C.fields?.length?C.fields:[""]).map(O=>({field:O,base:O?Xe(k(m.baseFlow,C.nodeId),O):k(m.baseFlow,C.nodeId),live:O?Xe(k(w,C.nodeId),O):k(w,C.nodeId),proposed:O?Xe(k(m.payload.flow,C.nodeId),O):k(m.payload.flow,C.nodeId)}))}));let $=f.merged?h?yt(f.merged).value:f.merged:m.payload.flow;return v.aiOverrideValues=(m.aiConflictOverrides??[]).map(C=>({nodeId:C.nodeId,fields:C.fields.map(O=>({field:O,live:O?Xe(k(w,C.nodeId),O):k(w,C.nodeId),proposed:O?Xe(k($,C.nodeId),O):k($,C.nodeId)}))})),v.liveChanges=cs(w,m.baseFlow??w),f.state!=="conflict"&&f.merged&&(v.vsCurrent=cs(h?yt(f.merged).value:f.merged,w)),v}if(c!==void 0){let u=l?cs(e.payload.flow,i):void 0;return Bi(e,{rev:e.expectedRev,flow:c},{currentRev:s,compareAgainst:"base",baseRevChanged:l,...u?{vsCurrent:u}:{}})}return Bi(e,{rev:s,flow:i},{compareAgainst:"current",baseSnapshotMissing:!0,baseRevChanged:l})}a(Yn,"computeProposalDiff");qe();lt();y();Ye();Te();y();var jl=require("path");function fs(e){return(0,jl.join)(e.userDir,".ai-access")}a(fs,"getStorageDir");var $m=["status:read","flows:read","proposals:create","proposals:read-own"];function qi(){return qn()}a(qi,"readPackageVersion");function Mm(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/1e3)):0}a(Mm,"secondsRemainingUntil");function Dm(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/6e4)):0}a(Dm,"minutesRemainingUntil");function Hm(e,t){let n=_e(t),r=Rt(t),o=`${n}/openapi.yaml`,s=`${n}/openapi.json`;return e==="rest"?{api:n,openapi:o,openapiJson:s,discovery:n}:e==="mcp"?{rpc:r,discovery:n}:{discovery:n}}a(Hm,"baseUrlsForTokenKind");function Zn(e,t){let n=no(e,t),r=Me(e.settings);if(t.auth){let o=t.auth.extra?.user;return{ok:!0,version:qi(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:t.auth.scopes.includes("propose")?"propose":"read",capabilities:{view:!0,manageLinks:!1,approveProposals:!1},defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,maxActiveLinks:r.maxActiveLinks,defaultDeploymentType:r.defaultDeploymentType,caller:{kind:"oauth",clientId:t.auth.clientId,user:typeof o=="string"?o:"owner",scopes:t.auth.scopes}}}if(t.accessLink){let o=Date.now();return{ok:!0,version:qi(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:null,capabilities:{view:!0,manageLinks:!1,approveProposals:!1},defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,maxActiveLinks:r.maxActiveLinks,defaultDeploymentType:r.defaultDeploymentType,caller:{kind:"access-link",linkId:t.accessLink.id,tokenKind:t.accessLink.kind,...t.accessLink.label!==void 0?{label:t.accessLink.label}:{},expiresAt:t.accessLink.expiresAt,minutesRemaining:Dm(t.accessLink.expiresAt,o),secondsRemaining:Mm(t.accessLink.expiresAt,o),permissions:[...$m],baseUrls:Hm(t.accessLink.kind,e.settings.httpAdminRoot)}}}return{ok:!0,version:qi(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:n.permission,capabilities:n.capabilities,storageDir:fs(e.settings),defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,maxActiveLinks:r.maxActiveLinks,defaultDeploymentType:r.defaultDeploymentType,caller:{kind:"admin",...n.permission?{permission:n.permission}:{}}}}a(Zn,"buildStatus");function Ul(e,t){let n=t.draftStore??new zo,r=t.rateLimiters??gl(t.rateLimit??e.settings.aiAccess),o=e.httpAdmin,s="/mcp/a/:token",i=[r.ip,wl(e,t.linkStore),r.token];o.use("/mcp/a",rl()),o.get(s,...i,D((u,p)=>{K(p),p.setHeader("X-Content-Type-Options","nosniff");let f=_e(e.settings.httpAdminRoot),h=u.accessLink;if(!h){B(p,401,"Access link required","access_link_missing");return}let m=cl({link:h,openapiPath:`${f}/openapi.yaml`,restApiBase:f,mcpRpcPath:Rt(e.settings.httpAdminRoot),publicOrigin:Fs(u)});p.status(200).type("text/markdown; charset=utf-8").send(m)})),o.get(`${s}/status`,...i,D((u,p)=>{K(p),p.json(Zn(e,u))})),o.get(`${s}/flows`,...i,D(async(u,p)=>{K(p);try{p.json(await vt(t.flowAdapter))}catch(f){let h=J(f);if(h){p.status(h.statusCode).json({...h.body,hint:"Retry after the owner fixes the plugin runtime."});return}B(p,502,f instanceof Error?f.message:String(f),"upstream_error")}})),o.get(`${s}/flows/:id`,...i,D(async(u,p)=>{K(p),await ts(t.flowAdapter,"get")(u,p)})),o.get(`${s}/nodes/:id`,...i,D(async(u,p)=>{K(p),await ns(t.flowAdapter,"get")(u,p)})),o.get(`${s}/nodes/:id/field/:name`,...i,D(async(u,p)=>{K(p),await rs(t.flowAdapter,"get")(u,p)})),o.get(`${s}/search`,...i,D(async(u,p)=>{K(p),await os(t.flowAdapter,"get")(u,p)}));let d={nodeTypes:t.nodeTypesAdapter,...t.catalogueFetcher?{catalogueFetcher:t.catalogueFetcher}:{},...t.catalogueUrl?{catalogueUrl:t.catalogueUrl}:{}};o.get(`${s}/node-types`,...i,D(async(u,p)=>{K(p),await ss(d)(u,p)})),o.get(`${s}/node-types/:type`,...i,D(async(u,p)=>{K(p),await is(d)(u,p)})),o.get(`${s}/palette/search`,...i,D(async(u,p)=>{K(p),await as(d)(u,p)}));let l=e.settings.httpAdminRoot;jm(o,s,i,t,l),o.get(`${s}/propose`,...i,D(Rl(t.proposalStore,t.auditLog,t.flowAdapter,l,t.nodeTypesAdapter))),qm(o,s,i,n,t,l);let c=vl();return o.all(`${s}/links`,c),o.all(`${s}/links/:id`,c),o.use("/mcp/a",bl()),n}a(Ul,"registerGetLinkRoutes");function jm(e,t,n,r,o){e.get(`${t}/proposals`,...n,D(async(s,i)=>{K(i);let d=s.accessLink;if(!d){B(i,401,"Access link required","access_link_missing");return}let l=Z(s,o),c=ve(s.query),u=Oo(s.query),p=await dt(r.flowAdapter),f=p?ze(r.proposalStore,p).expired:se(r.proposalStore),h=Dn(r.proposalStore,d,l,u);i.json({proposals:h.proposals.map(m=>be(r.proposalStore,d,m,{...l,include:c},{includeConflicts:!1})),...rn({...h,returned:h.proposals.length}),...me(f)})})),e.get(`${t}/proposals/:id`,...n,D(async(s,i)=>{K(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){B(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){B(i,401,"Access link required","access_link_missing");return}try{let c=Z(s,o),u=ve(s.query),p=await dt(r.flowAdapter),f=bt(r.proposalStore,l,d,c);i.json(be(r.proposalStore,l,f,{...c,include:u},{...p?{live:p}:{}}))}catch(c){let u=J(c,"get");if(u){i.status(u.statusCode).json({...u.body,hint:u.body.hint??"List proposals with GET \u2026/proposals."});return}z(i,c);return}})),Um(e,t,n,r,o),Bm(e,t,n,r,o)}a(jm,"registerGetLinkProposalRoutes");function Um(e,t,n,r,o){e.get(`${t}/proposals/:id/withdraw`,...n,D((s,i)=>{K(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){B(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){B(i,401,"Access link required","access_link_missing");return}let c=s.query.confirm;if(c!=="1"&&c!=="true"){B(i,400,"Withdraw over GET requires confirm=1","confirm_required",Ae("confirm_required","get"));return}let u=typeof s.query.reason=="string"?s.query.reason:void 0,p=Z(s,o),f=ve(s.query);try{let h=Hn(r.proposalStore,r.auditLog,l,d,u,p);i.json(be(r.proposalStore,l,h,{...p,include:f}))}catch(h){let m=J(h,"get");if(m){let g=m.body.code;B(i,m.statusCode,ut[g]??"Request failed",g,Ae(g,"get"));return}B(i,500,"Internal server error","internal_error");return}}))}a(Um,"registerGetLinkWithdrawRoute");function Bm(e,t,n,r,o){e.get(`${t}/proposals/:id/diff`,...n,D(async(s,i)=>{K(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){B(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){B(i,401,"Access link required","access_link_missing");return}try{let c=bt(r.proposalStore,l,d,Z(s,o)),u=j(s.query,"format"),p=zn(u==="json"||u==="md"?void 0:u);if(!p.ok){B(i,400,"Invalid diff format","invalid_diff_format","Valid formats: summary, full (default summary).");return}i.json(Gn(await Yn(c,r.flowAdapter,r.proposalStore.get(d)),p.format))}catch(c){let u=J(c);if(u){i.status(u.statusCode).json(u.body);return}z(i,c);return}}))}a(Bm,"registerGetLinkDiffRoute");function qm(e,t,n,r,o,s){e.get(`${t}/draft/new`,...n,D(Al(r,o.flowAdapter))),e.get(`${t}/draft/:draftId/append`,...n,D(xl(r))),e.get(`${t}/draft/:draftId/submit`,...n,D(Sl(r,o.proposalStore,o.auditLog,o.flowAdapter,s,o.nodeTypesAdapter)))}a(qm,"registerGetLinkDraftRoutes");y();de();y();var Bl=require("module");function Wm(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Bl.createRequire)(e)}a(Wm,"createPluginRequire");function zm(e){try{let r=Wm()("@node-red/runtime")._?.adminApi?.httpAdmin;if(r&&typeof r.use=="function"&&r!==e.httpAdmin)return r}catch{}return null}a(zm,"resolveEditorHttpAdmin");function ql(e){let t=[e.httpAdmin],n=zm(e);return n&&t.push(n),t}a(ql,"httpAdminAppsForMcpErrors");var Wl=Symbol.for("com55.ai-access.mcpJsonErrorHandler");function Gm(e){let t=e??"/";for(;t.endsWith("/");)t=t.slice(0,-1);let n=new Set;for(let r of t?["",t]:[""])for(let o of["/mcp","/mcp/rpc"])n.add(r+o),n.add(`${r+o}/`);return r=>n.has(r)}a(Gm,"mcpRpcMatcher");function Wi(e){return e.includes("/mcp")}a(Wi,"isMcpPath");function Km(e){return e.path||e.url||e.originalUrl||""}a(Km,"requestPath");function Jm(e){if(!e||typeof e!="object")return!1;let t=e;if(t.type==="entity.parse.failed"||e instanceof SyntaxError)return!0;let n=typeof e.message=="string"?e.message:"";return/JSON/i.test(n)&&(t.status===400||t.statusCode===400)}a(Jm,"isJsonParseError");function Vm(e){if(!e||typeof e!="object")return!1;let t=e;return t.type==="entity.too.large"||t.status===413||t.statusCode===413||t.message==="request entity too large"}a(Vm,"isPayloadTooLarge");function Ym(e){let t=Gm(e);return(n,r,o,s)=>{if(o.headersSent){s(n);return}let i=Km(r);if(Jm(n)&&Wi(i)){if(t(i)){o.status(400).json({jsonrpc:"2.0",error:{code:-32700,message:"Parse error"},id:null});return}o.status(400).json({error:"Invalid JSON body",code:"invalid_json"});return}if(Vm(n)&&Wi(i)){o.status(413).json({error:`Request body exceeds limit of ${Y} bytes`,code:"payload_too_large"});return}if(Wi(i)){if(t(i)){o.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:Wo(n).error,data:{code:"internal_error"}},id:null});return}o.status(500).json(Wo(n));return}s(n)}}a(Ym,"mcpJsonErrorHandler");function Zm(e,t){let n=e;n[Wl]||(n[Wl]=!0,e.use(Ym(t)))}a(Zm,"attachOnce");function zl(e){for(let t of ql(e))Zm(t,e.settings.httpAdminRoot)}a(zl,"attachMcpJsonErrorHandlers");y();y();y();Fr();var fn=class extends Error{static{a(this,"AccessLinkLimitError")}code="access_link_limit";statusCode=409;constructor(t){super(`Cannot create access link: at most ${t} active links are allowed. Revoke an existing link first.`),this.name="AccessLinkLimitError"}},hn=class extends Error{static{a(this,"AccessLinkNotFoundError")}code="access_link_not_found";statusCode=404;constructor(t){super(`Access link '${t}' was not found`),this.name="AccessLinkNotFoundError"}},Dr=class extends Error{static{a(this,"AccessLinkValidationError")}code="access_link_invalid";statusCode=400;constructor(t){super(t),this.name="AccessLinkValidationError"}},jt=class extends Error{static{a(this,"AccessLinkTtlError")}code="invalid_ttl";statusCode=400;hint=on;constructor(t){super(t),this.name="AccessLinkTtlError"}};y();var mn=require("crypto");Fr();function zi(e){return e==="get_link"||e==="rest"||e==="mcp"}a(zi,"isAccessLinkKind");function Gi(e){return e??"get_link"}a(Gi,"resolveAccessLinkKind");function Gl(e){return(0,mn.createHash)("sha256").update(e,"utf8").digest("hex")}a(Gl,"hashToken");function Xm(e,t){let n=Buffer.from(e,"utf8"),r=Buffer.from(t,"utf8");return n.length!==r.length?!1:(0,mn.timingSafeEqual)(n,r)}a(Xm,"hashesEqual");function Hr(e){let t={id:e.id,kind:Gi(e.kind),createdAt:new Date(e.createdAtMs).toISOString(),expiresAt:new Date(e.expiresAtMs).toISOString()};return e.label!==void 0&&(t.label=e.label),e.lastUsedAtMs!==void 0&&(t.lastUsedAt=new Date(e.lastUsedAtMs).toISOString()),e.createdBy!==void 0&&(t.createdBy=e.createdBy),t}a(Hr,"toPublic");var gn=class{static{a(this,"AccessLinkStore")}links=new Map;defaultTtlSeconds;maxActiveLinks;now;randomId;randomToken;constructor(t={}){this.defaultTtlSeconds=t.defaultTtlSeconds??14400,this.maxActiveLinks=t.maxActiveLinks??10,this.now=t.now??(()=>Date.now()),this.randomId=t.randomId??(()=>(0,mn.randomBytes)(12).toString("hex")),this.randomToken=t.randomToken??(()=>`aia_${(0,mn.randomBytes)(32).toString("base64url")}`)}configureDefaults(t){typeof t.defaultTtlSeconds=="number"&&Number.isFinite(t.defaultTtlSeconds)&&t.defaultTtlSeconds>0&&(this.defaultTtlSeconds=Math.floor(t.defaultTtlSeconds)),typeof t.maxActiveLinks=="number"&&Number.isFinite(t.maxActiveLinks)&&t.maxActiveLinks>0&&(this.maxActiveLinks=Math.floor(t.maxActiveLinks))}isActive(t,n){return t.revokedAtMs!==void 0?!1:t.expiresAtMs>n}list(){let t=this.now();return[...this.links.values()].filter(n=>this.isActive(n,t)).sort((n,r)=>r.createdAtMs-n.createdAtMs).map(Hr)}create(t={}){let n=this.now();if([...this.links.values()].filter(p=>this.isActive(p,n)).length>=this.maxActiveLinks)throw new fn(this.maxActiveLinks);let o=this.defaultTtlSeconds;if(t.ttlSeconds!==void 0){if(typeof t.ttlSeconds!="number"||!Number.isFinite(t.ttlSeconds)||t.ttlSeconds<=0)throw new jt("ttlSeconds must be a positive number");o=Math.floor(t.ttlSeconds)}let s=typeof t.label=="string"&&t.label.trim().length>0?t.label.trim():void 0,i=Gi(t.kind),d=Ci(i);if(o>d)throw new jt(Ei(i,d));let l=this.randomId(),c=this.randomToken(),u={id:l,tokenHash:Gl(c),kind:i,createdAtMs:n,expiresAtMs:n+o*1e3};return s!==void 0&&(u.label=s),t.createdBy!==void 0&&(u.createdBy=t.createdBy),this.links.set(l,u),{...Hr(u),token:c}}revoke(t){let n=this.links.get(t);if(!n)throw new hn(t);return n.revokedAtMs??=this.now(),Hr(n)}getById(t){if(typeof t!="string"||t.length===0)return null;let n=this.links.get(t);return n?Hr(n):null}validateResult(t){if(typeof t!="string"||t.length===0)return{status:"invalid"};let n=Gl(t),r=this.now();for(let o of this.links.values())if(Xm(o.tokenHash,n))return o.revokedAtMs!==void 0?{status:"invalid"}:o.expiresAtMs<=r?{status:"expired",expiresAt:new Date(o.expiresAtMs).toISOString()}:(o.lastUsedAtMs=r,{status:"active",link:Hr(o)});return{status:"invalid"}}validate(t){let n=this.validateResult(t);return n.status==="active"?n.link:null}size(){return this.links.size}clear(){this.links.clear()}upsertRecordForTest(t){let n={...t};delete n.token,this.links.set(t.id,n)}};Fr();Re();Te();function Ki(e,t){return t}a(Ki,"storeForRequest");function Jl(e){let t=typeof e.user?.username=="string"?e.user.username:void 0;return t?{kind:"admin",username:t}:{kind:"admin"}}a(Jl,"adminActor");function hs(e){let t=Me(e.settings);return new gn({defaultTtlSeconds:t.defaultLinkTtlSeconds,maxActiveLinks:t.maxActiveLinks})}a(hs,"createConfiguredLinkStore");function Vl(e){e.setHeader("Cache-Control","no-store"),e.setHeader("Pragma","no-cache")}a(Vl,"noStore");function Yl(e,t){return(n,r)=>{let s=Ki(e,t).list();Vl(r),r.json({links:s})}}a(Yl,"listLinksHandler");function Zl(e,t,n){return(r,o)=>{let s=r.body??{},i={};if(s.ttlSeconds!==void 0){if(typeof s.ttlSeconds!="number"){o.status(400).json({error:"ttlSeconds must be a positive number",code:"invalid_ttl",hint:on});return}i.ttlSeconds=s.ttlSeconds}if(typeof s.label=="string"&&(i.label=s.label),s.kind!==void 0){if(!zi(s.kind)){o.status(400).json({error:"kind must be one of: 'get_link', 'rest', 'mcp'",code:"invalid_link_kind"});return}i.kind=s.kind}typeof r.user?.username=="string"&&(i.createdBy=r.user.username);try{let d=Ki(e,t).create(i),l=d.kind;if(n){let c=Date.parse(d.createdAt),u=Date.parse(d.expiresAt),p=Number.isFinite(c)&&Number.isFinite(u)&&u>c?Math.round((u-c)/1e3):void 0;n.append("link.created",Jl(r),{linkId:d.id,label:d.label,expiresAt:d.expiresAt,createdAt:d.createdAt,kind:l,...p!==void 0?{ttlSeconds:p}:{}})}if(Vl(o),l==="get_link"){let c=Ia(r,e.settings.httpAdminRoot,d.token);o.status(201).json({...d,url:c});return}o.status(201).json({...d})}catch(d){if(d instanceof fn){o.status(d.statusCode).json({error:d.message,code:d.code});return}if(d instanceof jt){o.status(d.statusCode).json({error:d.message,code:d.code,hint:d.hint});return}if(d instanceof Dr){o.status(d.statusCode).json({error:d.message,code:d.code});return}z(o,d);return}}}a(Zl,"createLinkHandler");function Xl(e,t,n){return(r,o)=>{let s=r.params.id;if(typeof s!="string"||s.length===0){o.status(400).json({error:"Missing link id"});return}try{let i=Ki(e,t).revoke(s);if(n){let d=Date.parse(i.createdAt),l=Date.parse(i.expiresAt),c=Number.isFinite(d)&&Number.isFinite(l)&&l>d?Math.round((l-d)/1e3):void 0;n.append("link.revoked",Jl(r),{linkId:i.id,label:i.label,expiresAt:i.expiresAt,createdAt:i.createdAt,kind:i.kind,...c!==void 0?{ttlSeconds:c}:{}})}o.json({revoked:!0,link:i})}catch(i){if(i instanceof hn){o.status(i.statusCode).json({error:i.message,code:i.code});return}z(o,i);return}}}a(Xl,"revokeLinkHandler");y();function Qm(e){let t=Date.parse(e.createdAt),n=Date.parse(e.expiresAt);if(Number.isFinite(t)&&Number.isFinite(n)&&n>t)return Math.round((n-t)/1e3)}a(Qm,"ttlSecondsFromLink");function eg(e,t){if(!t||e.action!=="link.created"&&e.action!=="link.revoked")return e;let n={...e.details??{}},r=typeof n.linkId=="string"?n.linkId:"";if(!r)return e;let o=n.label==null||n.label==="",s=n.expiresAt==null||n.expiresAt==="",i=n.createdAt==null||n.createdAt==="",d=n.ttlSeconds==null;if(!o&&!s&&!i&&!d)return e;let l=t.getById(r);if(!l)return e;if(o&&l.label&&(n.label=l.label),s&&(n.expiresAt=l.expiresAt),i&&(n.createdAt=l.createdAt),d){let c=Qm(l);c!==void 0&&(n.ttlSeconds=c)}return{...e,details:n}}a(eg,"enrichAuditLogEntry");function Ql(e,t){return(n,r)=>{let o=n.query.limit,s=100;if(typeof o=="string"&&o.length>0){let d=Number(o);Number.isFinite(d)&&d>0&&(s=Math.min(Math.floor(d),500))}let i=e.list(s).map(d=>eg(d,t));r.json({logs:i})}}a(Ql,"listLogsHandler");y();var Vi=require("@modelcontextprotocol/sdk/server/mcp.js"),ac=require("@modelcontextprotocol/sdk/server/streamableHttp.js"),E=require("zod");tt();de();Gt();le();ct();Zt();en();qe();lt();y();var ec=require("async_hooks");Ne();var tg="ai.redacted",tc=new ec.AsyncLocalStorage;function nc(e,t,n,r){if(!(r<=0))try{e.append(tg,t,{redactions:r,operation:n})}catch{}}a(nc,"appendRedactionAudit");function rc(e){let t=tc.getStore();if(!t)return;let n=_o(e);n>0&&t.record(n)}a(rc,"noteResponseRedactions");function oc(e,t,n,r){return tc.run({record:a(o=>nc(e,t,n,o),"record")},r)}a(oc,"runWithRedactionAudit");function sc(e){let t=Array.isArray(e)?e[0]:e;if(t&&typeof t=="object"){let n=t;if(n.method==="tools/call"&&typeof n.params?.name=="string")return`mcp:${n.params.name.replace(/[^A-Za-z0-9_.-]/g,"").slice(0,64)}`;if(typeof n.method=="string")return`mcp:${n.method.slice(0,64)}`}return"mcp"}a(sc,"mcpOperationName");function Ji(e){let t=e.accessLink;if(t)return{kind:"access-link",linkId:t.id,...t.label!==void 0?{label:t.label}:{}}}a(Ji,"accessLinkAuditActor");function ic(e){return(t,n,r)=>{if(!t.path.startsWith("/mcp/")||t.path.startsWith("/mcp/rpc")){r();return}let o=n.json.bind(n);n.json=(s=>{let i=Ji(t);if(i&&n.statusCode<400){let d;try{d=String(JSON.stringify(s))}catch{d=""}let l=t.route?.path,c=`${t.method} ${typeof l=="string"?l:"unknown"}`;nc(e,i,c,_o(d))}return o(s)}),r()}}a(ic,"redactionAuditMiddleware");Ne();function dc(){return"0.2.0-beta.9"}a(dc,"pluginVersion");function pe(e){let t=JSON.stringify(e);return rc(t),{content:[{type:"text",text:t}]}}a(pe,"jsonResult");function Yi(e){return e===!0?new Set(["payload","baseFlow"]):new Set}a(Yi,"includeSetFromPayloadFlag");function fe(e,t=500){let n=Nr(e,"mcp");if(n)return{isError:!0,content:[{type:"text",text:JSON.stringify({...n.body,statusCode:n.statusCode})}]};let r=e instanceof Error?e.message:String(e);return{isError:!0,content:[{type:"text",text:JSON.stringify({error:r,code:"internal_error",statusCode:t})}]}}a(fe,"jsonError");var ng=new Set(["list_proposals","list_my_proposals"]),rg=new Set(["status","limit","offset","mine","flowId"]);function og(e){return!!e&&typeof e=="object"&&typeof e.safeParseAsync=="function"}a(og,"isZodLikeSchema");function sg(e,t,n){let r=t&&typeof t=="object"?t:{},o=n[0],s=o?.path&&o.path.length>0?String(o.path[0]):"(arguments)";if(ng.has(e)&&rg.has(s))return Mn(s,r[s]);let i=n.map(l=>({parameter:l.path&&l.path.length>0?l.path.map(String).join("."):"(arguments)",message:l.message??"Invalid value"})),d=i.length===1?`Invalid argument '${i[0]?.parameter}' for tool ${e}: ${i[0]?.message}`:`${i.length} invalid arguments for tool ${e}`;return new x(d,"invalid_arguments",{details:{parameter:s,errors:i,hint:`Fix '${s}' to match the ${e} input schema (see tools/list).`}})}a(sg,"mcpArgumentError");function lc(e){let t=e,n=t.validateToolInput;typeof n=="function"&&(t.validateToolInput=async(r,o,s)=>{let i=r.inputSchema;if(!og(i))return n.call(e,r,o,s);let d=await i.safeParseAsync(o??{});if(d.success)return d.data;let l=Nr(sg(s,o,d.error.issues),"mcp"),c=l?{...l.body,statusCode:l.statusCode}:{};throw new Error(JSON.stringify(c))})}a(lc,"installMcpArgumentErrorMapping");function cc(e,t,n,r){e.registerTool("get_status",{description:'Return plugin status (version, AI feature flags, storage path, deploy defaults). For access-link callers, permission is null and caller.kind is "access-link" \u2014 that is expected, not an error. Use before other tools to confirm the plugin is enabled.'},()=>{try{return pe(Zn(t,r))}catch(o){return fe(o)}}),e.registerTool("list_flows",{description:'List all flows as a flat Admin API v2 array plus the current rev string. Likely secrets are shown as "[REDACTED]" (nodes list redacted paths in _aiRedactions). Returns { rev, flows }. Call this (or get_flow) before submit_proposal so expectedRev matches.'},async()=>{try{return pe(await vt(n.flowAdapter))}catch(o){return fe(o,502)}}),e.registerTool("get_flow",{description:"Get one tab/subflow by id with nested nodes/configs and the current rev. Pass summary=true for a lightweight node list (id/type/name/wires/bytes, no field contents). Prefer summary \u2192 search_flows \u2192 get_node_field(lines) \u2192 replace_text \u2192 submit_proposal. Returns { rev, flow } or summary shape. Use rev as expectedRev when submitting.",inputSchema:{id:E.z.string().min(1).describe("Flow / tab id"),summary:E.z.boolean().optional().describe("When true, return node list with sizes only (no field contents)")}},async({id:o,summary:s})=>{try{return pe(s?await Vo(n.flowAdapter,o):await Ge(n.flowAdapter,o))}catch(i){return fe(i,502)}}),e.registerTool("get_node",{description:"Get one full node by id (includes owning flowId when known). Optional flow scopes the lookup.",inputSchema:{id:E.z.string().min(1).describe("Node id"),flow:E.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,flow:s})=>{try{return pe(await Yo(n.flowAdapter,o,s))}catch(i){return fe(i,502)}}),e.registerTool("get_node_field",{description:'Get one string field from a node. Optional lines="A-B" (1-based inclusive) returns a slice; response always includes totalLines and bytes.',inputSchema:{id:E.z.string().min(1).describe("Node id"),field:E.z.string().min(1).describe("Field name (e.g. template, func)"),lines:E.z.string().optional().describe("Optional 1-based line range A or A-B"),flow:E.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,field:s,lines:i,flow:d})=>{try{return pe(await Zo(n.flowAdapter,o,s,{...d?{flowId:d}:{},...i?{lines:i}:{}}))}catch(l){return fe(l,502)}}),e.registerTool("search_flows",{description:"Text search across string fields. Returns hits with node id, field, line numbers, and a short snippet. Results are capped; truncated=true when the cap is hit. Optional flow scopes to one tab.",inputSchema:{q:E.z.string().describe("Substring to find (case-sensitive; non-empty required)"),flow:E.z.string().min(1).optional().describe("Optional tab id")}},async({q:o,flow:s})=>{try{return pe(await Xo(n.flowAdapter,o,s?{flowId:s}:{}))}catch(i){return fe(i,502)}}),e.registerTool("list_node_types",{description:"List installed Node-RED palette node types (type, module, category, description, \u2026). Optional q filters type/module/category/description (case-insensitive). Results are capped. Call this before proposing nodes with unfamiliar types.",inputSchema:{q:E.z.string().optional().describe("Optional case-insensitive filter")}},async({q:o})=>{try{return pe(await bo(n.nodeTypesAdapter,o?{q:o}:{}))}catch(s){return fe(s,502)}}),e.registerTool("get_node_type",{description:"Get detail for one installed node type: defaults (JSON), inputs/outputs (always set), requiredConfigNodeTypes, help sections (summary/inputs/outputs), category/module/color. Types are case-sensitive. Pass includeHelp=false to omit help. 404 node_type_not_found when unknown (didYouMean on case mismatch; else nearest names).",inputSchema:{type:E.z.string().min(1).describe("Node type id (e.g. inject, template) \u2014 case-sensitive"),includeHelp:E.z.boolean().optional().describe("When false, omit help sections (default true)")}},async({type:o,includeHelp:s})=>{try{return pe(await Yt(n.nodeTypesAdapter,o,{includeHelp:s!==!1}))}catch(i){return fe(i,502)}}),e.registerTool("search_palette",{description:"Search the public Node-RED catalogue for modules to recommend. Returns module id, description, version, updated, link, and installed boolean (cross-checked against installed modules). Core node-red types are not in the catalogue \u2014 use list_node_types. RECOMMEND ONLY \u2014 never installs.",inputSchema:{q:E.z.string().describe("Search string (id / description / keywords; non-empty required)")}},async({q:o})=>{try{return pe(await vo(n.nodeTypesAdapter,o,{...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{}}))}catch(s){return fe(s,502)}})}a(cc,"registerReadTools");function uc(e,t,n,r,o){let s=Z(r,t.settings.httpAdminRoot);e.registerTool("submit_proposal",{description:`Submit a replace_flow proposal for owner approval (does not deploy). Preferred for small string edits: payload: { flowId, nodes: [{ op:"replace_text", node, field, find, replace, occurrence? }] } \u2014 find must be unique unless occurrence (1-based) is set; multiple replace_text ops apply in order; yields a short readable diff. Or send a full tab: payload.flow from get_flow (id, type, label?, nodes[], configs?). Inputs (all top-level, next to each other): type="replace_flow", expectedRev (required string from list_flows/get_flow rev; expected_rev alias accepted), optional title (\u226480 chars), and payload: { flowId, flow? } and/or payload.nodes / top-level nodes. Do not nest expectedRev inside payload. Secret values appear as "[REDACTED]" in reads; leave the placeholder unchanged to keep the stored secret (it is restored on submit), or replace it with a new value to change it. Payload size limit is ${Y} bytes (413 if exceeded). Recent known expectedRev snapshots are accepted and auto-rebased; unknown revisions return 409 version_mismatch. Open proposals auto-rebase on revision changes; conflict blocks approval until resolved. Proposals expire after the configured TTL (default 7 days). replace_text errors: 400 text_not_found; 409 ambiguous_match with matchCount. Unknown palette types return 400 unknown_node_type with nearest matches \u2014 call list_node_types / search_palette and ask the user to install via Manage palette (never auto-install). Returns a lean proposal (id, status, title, storedTitle, flowLabel, displayName, reviewUrl, changeSummary, \u2026) by default; title may gain " in <flow>"; storedTitle is the title as saved (your title without that suffix, or the auto-generated title, which may already name the flow). Pass include_payload=true to include payload and baseFlow. When telling the user about the proposal, cite displayName and reviewUrl \u2014 not the raw id. Humans approve on the /ai-access dashboard (reviewUrl opens/scrolls to it).`,inputSchema:{type:E.z.literal("replace_flow").describe("Must be 'replace_flow'"),expectedRev:E.z.string().min(1).optional().describe("Top-level current flow revision from get_flow/list_flows (required at submit; sit next to type and payload, not inside payload)"),expected_rev:E.z.string().min(1).optional().describe("Alias for expectedRev (snake_case); prefer expectedRev"),title:E.z.string().max(80).optional().describe("Optional short human title (\u226480 chars). Auto-generated from changes when omitted."),include_payload:E.z.boolean().optional().describe("When true, include payload and baseFlow in the response (default lean)"),nodes:E.z.array(E.z.record(E.z.string(),E.z.unknown())).optional().describe("Optional node ops (set/add/remove/replace_text). Same as payload.nodes when flow is omitted."),payload:E.z.object({flowId:E.z.string().min(1).describe("Tab id to replace"),flow:E.z.object({id:E.z.string().optional(),type:E.z.string().optional().describe("Usually 'tab' (included by get_flow)"),label:E.z.string().optional(),nodes:E.z.array(E.z.record(E.z.string(),E.z.unknown())).optional().describe("Child nodes for the tab (from get_flow.nodes)"),configs:E.z.array(E.z.record(E.z.string(),E.z.unknown())).optional()}).passthrough().optional().describe("Full tab from get_flow; omit when using nodes ops"),nodes:E.z.array(E.z.record(E.z.string(),E.z.unknown())).optional().describe("Node ops applied to the live tab (prefer replace_text for small string edits)")}).describe("Replacement flow payload and/or node ops")}},async i=>{try{let d=await Ot(n.proposalStore,n.auditLog,o,{...i,expectedRev:typeof i.expectedRev=="string"&&i.expectedRev.length>0?i.expectedRev:i.expected_rev},n.flowAdapter,s,n.nodeTypesAdapter),l={...ue(d,{...s,include:Yi(i.include_payload)}),mine:!0},c=se(n.proposalStore),u=at(it(n.proposalStore,n.proposalStore.get(d.id),o.id));return pe({...l,...u,...me(c)})}catch(d){return fe(d)}}),ag(e,n,o,s),dg(e,n,o,s)}a(uc,"registerProposalTools");function ig(e,t,n,r){let o={mine:E.z.boolean().optional().describe("true: only proposals created by this access link; false: only other links\u2019 proposals"),status:E.z.union([E.z.string(),E.z.array(E.z.string())]).optional().describe("Filter by proposal status: one value, an array, or a comma-separated string (pending, conflict, approved, rejected, failed, withdrawn, expired, superseded). An empty array or empty string means no status filter."),flowId:E.z.string().optional().describe("Filter by flow tab id (payload.flowId)"),limit:E.z.number().int().min(1).max($n).optional().describe(`Page size (1\u2013${$n}). Omit to return all matches.`),offset:E.z.number().int().min(0).optional().describe("Skip this many filtered results (default 0)"),include_payload:E.z.boolean().optional().describe("When true, include payload and baseFlow on each proposal")},s=a(async i=>{try{let d=Yi(i.include_payload),l=await dt(t.flowAdapter),c=l?ze(t.proposalStore,l).expired:se(t.proposalStore),u={};typeof i.mine=="boolean"&&(u.mine=i.mine);let p=Ti(i.status);p&&(u.status=p),typeof i.flowId=="string"&&i.flowId.length>0&&(u.flowId=i.flowId),typeof i.limit=="number"&&(u.limit=i.limit),typeof i.offset=="number"&&(u.offset=i.offset);let f=Dn(t.proposalStore,n,r,u);return pe({proposals:f.proposals.map(h=>be(t.proposalStore,n,h,{...r,include:d},{includeConflicts:!1})),...rn({...f,returned:f.proposals.length}),...me(c)})}catch(d){return fe(d)}},"listProposalsHandler");e.registerTool("list_proposals",{description:"List proposals visible to this access link (all proposals by default). Each item includes mine:true|false for the caller. Filter with mine, status, and/or flowId; paginate with limit/offset (response includes total, offset, hasMore, nextOffset). Invalid filters return invalid_filter. Returns lean { proposals, total, \u2026 } with title, displayName, reviewUrl, changeSummary (no payload/baseFlow unless include_payload=true). Prefer displayName + reviewUrl when referring to a proposal with the user (not the raw id).",inputSchema:o},s),e.registerTool("list_my_proposals",{description:"Alias of list_proposals. Prefer list_proposals. Lists proposals visible to this access link with optional mine/status/flowId filters and limit/offset pagination.",inputSchema:o},s)}a(ig,"registerListProposalsTools");function ag(e,t,n,r){ig(e,t,n,r),e.registerTool("get_proposal",{description:"Get any proposal by id (all tokens can read). Response includes mine:true|false and, for open proposals (pending or conflict), conflictingProposals when other open work touches the same flow. baseRev aliases expectedRev; baseRevChanged is informational. rebase.state=clean|merged means mergeable; conflict blocks approval. Lean by default (no payload/baseFlow); pass include_payload=true for full bodies. Cite displayName + reviewUrl when talking to the user.",inputSchema:{id:E.z.string().min(1).describe("Proposal id (prop-\u2026)"),include_payload:E.z.boolean().optional().describe("When true, include payload and baseFlow")}},async({id:o,include_payload:s})=>{try{let i=await dt(t.flowAdapter),d=bt(t.proposalStore,n,o,r);return pe(be(t.proposalStore,n,d,{...r,include:Yi(s)},{...i?{live:i}:{}}))}catch(i){return fe(i)}})}a(ag,"registerProposalQueryTools");function dg(e,t,n,r){e.registerTool("get_proposal_diff",{description:"Diff any proposal against its stored baseFlow snapshot (what changed). Optional format=summary|full (default summary). Open proposals include rebase and liveChanges; conflict blocks approval, otherwise vsCurrent shows the merged deployment.",inputSchema:{id:E.z.string().min(1).describe("Proposal id (prop-\u2026)"),format:E.z.enum(["summary","full"]).optional().describe("Diff payload format (default summary)")}},async({id:o,format:s})=>{try{let i=bt(t.proposalStore,n,o),d=zn(s??"summary");if(!d.ok)return fe(new x("Invalid diff format","invalid_diff_format",{details:{hint:"Valid formats: summary, full (default summary)."}}));let l=Gn(await Yn(i,t.flowAdapter,t.proposalStore.get(o)),d.format);return pe(l)}catch(i){return fe(i)}}),e.registerTool("resolve_proposal_conflict",{description:"Resolve your own open proposal. Different node/tab fields auto-merge; only fields changed on both sides conflict (x/y never conflict). Choose one current conflict using nodeId + choice, preserving all non-conflicting fields. Alternatively replace the proposal using expectedRev + payload: nodes ops apply to the flow snapshot at expectedRev, NOT the stored proposal or merged result; include every intended proposal edit. Read get_proposal_diff for conflicts and get_flow for a fresh rev. These two modes cannot be mixed.",inputSchema:{id:E.z.string().min(1).describe("Your open proposal id (prop-\u2026)"),nodeId:E.z.string().min(1).optional().describe("Conflict nodeId from rebase.conflicts; use the flow id for tab/env conflicts. Required with choice."),choice:E.z.enum(["live","proposed"]).optional().describe("For this conflict, keep live or proposed values of overlapping fields only. Disjoint edits survive. Use with nodeId, without expectedRev/title/payload."),expectedRev:E.z.string().min(1).optional().describe("Revision of the flow snapshot on which replacement payload ops run. Required with payload; normally the current get_flow rev."),title:E.z.string().max(80).optional().describe("Optional replacement title (body replacement mode only)"),payload:E.z.object({flowId:E.z.string().min(1).describe("Target flow id; must remain the proposal\u2019s flow id"),flow:E.z.record(E.z.string(),E.z.unknown()).optional().describe("Full replacement tab based on expectedRev; preserve all desired edits. Use either flow or nodes."),nodes:E.z.array(E.z.record(E.z.string(),E.z.unknown())).optional().describe("set/add/remove/replace_text operations applied to the flow at expectedRev, not to the stored proposal. Env merges by unique name and credentials by key; same-field edits conflict.")}).optional().describe("Replacement body; required with expectedRev, omitted with nodeId + choice")}},async({id:o,...s})=>{try{let i=await $o(t.proposalStore,t.auditLog,n,o,s,t.flowAdapter,r,t.nodeTypesAdapter);return pe(be(t.proposalStore,n,i,r))}catch(i){return fe(i)}}),e.registerTool("withdraw_proposal",{description:"Withdraw one of YOUR open (pending or conflict) proposals (status becomes withdrawn, not rejected). Optional reason. Only the creating access link can withdraw via MCP; other tokens get 403. If it is lost, a full admin session can recover via POST /proposals/{id}/withdraw. Cite displayName + reviewUrl when telling the user.",inputSchema:{id:E.z.string().min(1).describe("Proposal id (prop-\u2026) owned by this access link"),reason:E.z.string().optional().describe("Optional withdraw reason")}},({id:o,reason:s})=>{try{let i=Hn(t.proposalStore,t.auditLog,n,o,s,r);return pe(be(t.proposalStore,n,i,r))}catch(i){return fe(i)}})}a(dg,"registerProposalActionTools");function lg(e,t,n){let r=new Vi.McpServer({name:"node-red-ai-access",version:dc()});lc(r);let o=n.accessLink;if(!o)throw new Error("Access link required");return cc(r,e,t,n),uc(r,e,t,n,o),r}a(lg,"createAiMcpServer");function cg(e){let t=Ji(e);if(t)return t;let n=e.auth?.clientId??"",r=typeof e.auth?.extra?.user=="string"?e.auth.extra.user:"owner";return{kind:"access-link",linkId:fo(n,r),label:"oauth"}}a(cg,"mcpAuditActor");function Zi(e,t){return async(n,r,o)=>{try{if(!n.accessLink&&!n.auth){r.setHeader("WWW-Authenticate",ge),r.status(401).json({error:He,code:ht});return}let s={...t,flowAdapter:Lt(t.flowAdapter)},i=n.accessLink?lg(e,s,n):ug(e,s,n),d=new ac.StreamableHTTPServerTransport({enableJsonResponse:!0});await i.connect(d),await oc(t.auditLog,cg(n),sc(n.body),()=>d.handleRequest(n,r,n.body)),r.on("close",()=>{d.close(),i.close()})}catch(s){if(!r.headersSent){z(r,s);return}o(s instanceof Error?s:new Error(String(s)))}}}a(Zi,"mcpRpcPostHandler");function ug(e,t,n){let r=new Vi.McpServer({name:"node-red-ai-access",version:dc()});if(lc(r),cc(r,e,t,n),(n.auth?.scopes??[]).includes("propose")){let s=typeof n.auth?.extra?.user=="string"?n.auth.extra.user:"owner",i=n.auth?.clientId??"",l={id:fo(i,s),kind:"mcp",createdAt:new Date().toISOString(),expiresAt:new Date((n.auth?.expiresAt??0)*1e3).toISOString(),createdBy:s,oauthClientId:i,oauthUser:s};uc(r,e,t,n,l)}return r}a(ug,"createOAuthMcpServer");function jr(e,t){t.status(405).set("Allow","POST").json({jsonrpc:"2.0",error:{code:-32e3,message:"Method not allowed. Use POST for Streamable HTTP."},id:null})}a(jr,"mcpRpcMethodNotAllowed");y();var Ur=require("crypto"),Qn=require("fs"),ta=require("path"),ne=require("@modelcontextprotocol/sdk/server/auth/errors.js"),gc=require("@modelcontextprotocol/sdk/server/auth/handlers/authorize.js"),na=require("@modelcontextprotocol/sdk/server/auth/handlers/metadata.js"),yc=require("@modelcontextprotocol/sdk/server/auth/handlers/register.js"),wc=require("@modelcontextprotocol/sdk/server/auth/handlers/revoke.js"),bc=require("@modelcontextprotocol/sdk/server/auth/handlers/token.js"),vc=require("@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js"),kc=require("@modelcontextprotocol/sdk/server/auth/router.js"),Ac=rr(require("express"),1);de();Ye();y();function pt(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t]??t)}a(pt,"esc");var pc={read:'<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5c-5 0-9 4.5-10 7 1 2.5 5 7 10 7s9-4.5 10-7c-1-2.5-5-7-10-7Zm0 11.5a4.5 4.5 0 1 1 0-9 4.5 4.5 0 0 1 0 9Zm0-2.3a2.2 2.2 0 1 0 0-4.4 2.2 2.2 0 0 0 0 4.4Z"/></svg>',propose:'<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 17.2V20h2.8l8.3-8.3-2.8-2.8L4 17.2Zm13.7-7.6a1 1 0 0 0 0-1.4l-1.9-1.9a1 1 0 0 0-1.4 0l-1.4 1.4 2.8 2.8 1.9-1.9Z"/></svg>'},pg={read:["Read your flows","View flows, nodes, node types and AI Access logs."],propose:["Propose flow changes","Submit change proposals. Nothing is deployed until you approve it in Node-RED."]},Xi='<svg class="logo" viewBox="0 0 48 48" aria-hidden="true"><rect width="48" height="48" rx="11" fill="#8f0000"/><path d="M11 15h9a4 4 0 0 1 4 4v10a4 4 0 0 0 4 4h9" fill="none" stroke="#fff" stroke-width="3" stroke-linecap="round"/><rect x="6" y="11" width="9" height="8" rx="2.5" fill="#fff"/><rect x="33" y="29" width="9" height="8" rx="2.5" fill="#fff"/><circle cx="37.5" cy="16" r="5" fill="none" stroke="#fff" stroke-width="2.4"/><path d="M37.5 13.5v5M35 16h5" stroke="#fff" stroke-width="2.2" stroke-linecap="round"/></svg>',fg=`
|
|
991
991
|
:root{color-scheme:light;--bg:#f3f4f6;--card:#fff;--text:#111827;--muted:#4b5563;--line:#e5e7eb;--soft:#f9fafb;--brand:#8f0000;--brand-2:#b91c1c;--ok:#0f766e;--warn-bg:#fff7ed;--warn:#9a3412;--focus:#2563eb}
|
|
992
992
|
*{box-sizing:border-box}
|
|
993
993
|
body{margin:0;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px 16px;background:var(--bg);color:var(--text);font:15px/1.5 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif}
|
|
@@ -1023,7 +1023,7 @@ form.login input{font:inherit;padding:9px 11px;border-radius:10px;border:1px sol
|
|
|
1023
1023
|
a{color:var(--focus)}
|
|
1024
1024
|
[hidden]{display:none!important}
|
|
1025
1025
|
@media (max-width:420px){.actions{flex-direction:column-reverse}.secondary{flex:1}}
|
|
1026
|
-
`;function
|
|
1026
|
+
`;function fc(e){let t=e.scopes.map(n=>{let[r,o]=pg[n]??[n,""];return`<li data-scope="${pt(n)}">${pc[n]??pc.read}<div><b>${pt(r)}</b><span>${pt(o)}</span></div></li>`}).join("");return`<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="referrer" content="no-referrer"><title>Authorize MCP client \xB7 Node-RED AI Access</title><style>${fg}</style></head><body><main class="card" data-request="${pt(e.requestId)}" data-token-key="${pt(e.tokenKey)}" data-admin-root="${pt(e.adminRoot)}"><div class="head">${Xi}<div class="brand"><b>Node-RED AI Access</b><span>Authorization request</span></div></div><div class="body"><h1><span class="app">${pt(e.redirectHost)}</span> wants to access your Node-RED</h1><p class="sub">Access tokens will be sent to <span class="host">${pt(e.redirectHost)}</span>. App name (provided by the client, unverified): <b>${pt(e.clientName)}</b></p>${e.loopbackRedirect?'<p class="warn">Warning: this redirect address is reachable only on this machine.</p>':""}<ul class="perms" aria-label="Requested permissions">${t}</ul><p id="status" class="status" role="status" aria-live="polite">Checking your Node-RED session\u2026</p><form id="login" class="login" hidden autocomplete="on"><label>Username<input id="username" name="username" autocomplete="username" required></label><label>Password<input id="password" name="password" type="password" autocomplete="current-password" required></label><div class="actions"><button id="login-submit" class="primary" type="submit">Sign in</button></div></form><p id="grant" class="sub" hidden></p><p id="user" class="user" hidden><span class="avatar" id="avatar" aria-hidden="true"></span><span>Signed in as <b id="username-label"></b></span></p><div class="actions"><button id="deny" class="secondary" type="button" hidden>Deny</button><button id="allow" class="primary" type="button" hidden>Allow access</button><button id="retry" class="secondary" type="button" hidden>Retry</button></div></div><div class="foot">You can revoke this app at any time from the AI Access dashboard (OAuth tab) in Node-RED.</div></main><script nonce="${pt(e.nonce)}">${e.script}</script></body></html>`}a(fc,"renderConsentPage");y();y();var ms=`
|
|
1027
1027
|
function nrLoginType(adminRootHref) {
|
|
1028
1028
|
return fetch(new URL('auth/login', adminRootHref).href, {
|
|
1029
1029
|
headers: { 'Node-RED-API-Version': 'v2' }, credentials: 'same-origin'
|
|
@@ -1049,8 +1049,8 @@ a{color:var(--focus)}
|
|
|
1049
1049
|
return { ok: true };
|
|
1050
1050
|
});
|
|
1051
1051
|
}
|
|
1052
|
-
`;var
|
|
1053
|
-
${
|
|
1052
|
+
`;var hc=`(function () {
|
|
1053
|
+
${ms}
|
|
1054
1054
|
var main = document.querySelector('main');
|
|
1055
1055
|
var id = main.getAttribute('data-request');
|
|
1056
1056
|
var key = main.getAttribute('data-token-key');
|
|
@@ -1174,7 +1174,7 @@ ${os}
|
|
|
1174
1174
|
document.addEventListener('visibilitychange', function () { if (document.visibilityState === 'visible') autoRetry(false); });
|
|
1175
1175
|
window.addEventListener('focus', function () { autoRetry(false); });
|
|
1176
1176
|
load();
|
|
1177
|
-
})();`;ke();var Lt=a(e=>(0,Ir.createHash)("sha256").update(e).digest("hex"),"hash"),ln=a(()=>(0,Ir.randomBytes)(32).toString("base64url"),"secret"),Vm=36e5,Ym=25,Xm=5,Zm=500,Yl=a(()=>({clients:[],codes:[],tokens:[]}),"emptyDb"),ss=class{constructor(t,n){this.maxClients=n;this.path=(0,Mi.join)(t.root,"oauth.json");try{let r=(0,Hn.existsSync)(this.path)?JSON.parse((0,Hn.readFileSync)(this.path,"utf8")):Yl();if(!r||typeof r!="object"||!("clients"in r)||!("codes"in r)||!("tokens"in r)||!Array.isArray(r.clients)||!Array.isArray(r.codes)||!Array.isArray(r.tokens))throw new Error("Invalid OAuth store");this.db=r}catch{this.db=Yl()}(0,Hn.mkdirSync)(t.root,{recursive:!0,mode:448}),this.prune()}static{a(this,"OAuthStore")}db;path;save(){cn((0,Mi.join)(this.path,".."),this.path,ft(this.db,8e6,{kind:"oauth",pretty:!0}))}prune(t=Date.now()){let n=this.db.clients.length+this.db.codes.length+this.db.tokens.length,r=new Set(this.db.clients.filter(o=>o.approved?(o.lastUsedAt??(o.client_id_issued_at??0)*1e3)>t-30*864e5:(o.client_id_issued_at??0)*1e3>t-Vm).map(o=>o.client_id));this.db.clients=this.db.clients.filter(o=>r.has(o.client_id)),this.db.codes=this.db.codes.filter(o=>o.expires>t&&r.has(o.clientId)),this.db.tokens=this.db.tokens.filter(o=>r.has(o.clientId)&&o.expires>t),this.db.clients.length+this.db.codes.length+this.db.tokens.length!==n&&this.save()}getClient(t){return this.prune(),this.db.clients.find(n=>n.client_id===t)}registerClient(t){if(this.prune(),this.approvedCount()>=this.maxClients())throw new Q.InvalidClientMetadataError("Maximum active OAuth clients reached");if(this.db.clients.length-this.approvedCount()>=Ym)throw new Q.InvalidClientMetadataError("Too many OAuth clients awaiting consent");if(!Array.isArray(t.redirect_uris)||!t.redirect_uris.length||t.redirect_uris.some(r=>{try{let o=new URL(r);return!!o.username||!!o.password||!!o.hash||o.protocol!=="https:"&&!(o.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(o.hostname))}catch{return!0}}))throw new Q.InvalidClientMetadataError("Redirect URIs must use HTTPS or loopback HTTP");if(t.token_endpoint_auth_method&&t.token_endpoint_auth_method!=="none")throw new Q.InvalidClientMetadataError("Only public clients are supported");let n={...t,client_secret:void 0,client_secret_expires_at:void 0,client_id:ln(),client_id_issued_at:Math.floor(Date.now()/1e3),token_endpoint_auth_method:"none",lastUsedAt:Date.now()};return this.db.clients.push(n),this.save(),n}approvedCount(){return this.db.clients.filter(t=>t.approved).length}approve(t){let n=this.db.clients.find(r=>r.client_id===t);if(!(!n||n.approved)){if(this.approvedCount()>=this.maxClients())throw new Q.InvalidClientMetadataError("Maximum active OAuth clients reached");n.approved=!0,this.save()}}get data(){return this.db}persist(){this.touchTimer&&(clearTimeout(this.touchTimer),this.touchTimer=void 0),this.save()}touchTimer;touch(){this.touchTimer||(this.touchTimer=setTimeout(()=>{this.touchTimer=void 0,this.save()},5e3),this.touchTimer.unref?.())}revokeClient(t){let n=this.db.clients.length;return this.db.clients=this.db.clients.filter(r=>r.client_id!==t),this.db.tokens=this.db.tokens.filter(r=>r.clientId!==t),this.db.codes=this.db.codes.filter(r=>r.clientId!==t),this.save(),this.db.clients.length!==n}};function ji(e){switch(e.source){case"http-in":return`Route ${e.path} is already used by http-in node '${e.nodeName??e.nodeId??"unnamed"}'`;case"httpStatic":return`File ${e.path} is served by httpStatic`;case"middleware":return`Path prefix is already handled by mounted middleware (${e.layer??"unknown"})`;default:return`Route ${e.path} is already registered by another handler (${e.layer??"route"})`}}a(ji,"describeOAuthRouteCollision");var Ui=a((e,t)=>e.toLowerCase().startsWith(t.toLowerCase()),"startsWithCi");function Oi(e){return e===!1?void 0:`/${typeof e=="string"?e:"/"}`.replace(/\/+/g,"/").replace(/\/$/,"")}a(Oi,"normalizeRoot");function $i(e,t){if(e!==void 0){if(!e)return t;if(Ui(t,`${e}/`))return t.slice(e.length)}}a($i,"relativePrefix");function Qm(e){let t=new Map;for(let r of e)r.id&&t.set(r.id,r);let n=a((r,o=new Set)=>{if(!r)return!0;let s=t.get(r);return s?s.type==="tab"?!s.disabled&&!s.d:s.type!=="subflow"||o.has(r)?!1:(o.add(r),e.some(i=>i.type===`subflow:${r}`&&!i.d&&n(i.z,o))):!0},"live");return n}a(Qm,"makeLiveCheck");function eg(e,t){if(!e||!t.length)return[];let n=[];try{e(s=>n.push(s))}catch{return[]}let r=Qm(n),o=[];for(let s of n){if(s.type!=="http in"||typeof s.url!="string"||s.d)continue;let i=s.url.startsWith("/")?s.url:`/${s.url}`;!t.some(d=>Ui(i,d))||!r(s.z)||o.push({path:i,source:"http-in",nodeId:s.id,nodeName:s.name?s.name:void 0,flowId:s.z})}return o}a(eg,"findHttpInCollisions");var tg=new Set(["bound dispatch","handle"]),ng=["authorize","token","register","revoke"],rg=["authorization-server","protected-resource"];function og(e){let t=e.length;for(;t>0&&e.charCodeAt(t-1)===47;)t--;return e.slice(0,t)}a(og,"trimTrailingSlashes");function sg(e){let t=new Set;return e.filter(n=>{let r=`${n.source}\0${n.path.toLowerCase()}`;return t.has(r)?!1:(t.add(r),!0)})}a(sg,"dedupe");function oc(e){let t=Oi(e.httpAdminRoot),n=e.httpNode?Oi(e.httpNodeRoot):void 0,r=`${t??""}/mcp/oauth/`,o=e.metadataFallback?`${t??""}/mcp/.well-known/oauth-`:`${n??""}/.well-known/oauth-`,s=a(p=>{let f=$i(t,p),h=$i(n,p),m=h?eg(e.eachNode,[h]):[],g=new Set(m.map(x=>x.path.toLowerCase())),y=h?Hi(e.httpNode,[h]).filter(x=>!(x.source==="route"&&tg.has(x.layer??"")&&g.has(x.path.toLowerCase()))):[],A=f?Hi(e.httpAdmin,[f]):[];return sg([...A,...y,...m])},"scan"),i=s(r),d=s(o),l=e.fileExists??(()=>!1),c=(e.staticDirs??[]).map(p=>typeof p=="string"?{path:p,root:e.httpStaticRoot}:p),u=a((p,f)=>{for(let h of c){let m=Oi(h.root),g=og(h.path);for(let y of p){let A=$i(m,y);!A||!l(`${g}${A}`)||f.push({path:y,source:"httpStatic",file:`${g}${A}`})}}},"checkStatic");return u(ng.map(p=>`${r}${p}`),i),u(rg.map(p=>`${o}${p}`),d),{authorization:i,metadata:d}}a(oc,"scanOAuthRouteCollisions");function ig(e){let t=a(n=>{try{return e?.[n]}catch{return}},"read");return t("_router")?.stack??t("router")?.stack??[]}a(ig,"routerStack");function ag(e){let t=e.route?.path;return t?(Array.isArray(t)?t:[t]).map(String):[]}a(ag,"layerPaths");function dg(e,t){if(e.name==="router")return!1;let n=e.regexp,r=e.matchers??(n?[s=>n.test(s)]:[]);if(!r.length)return!1;let o=a(s=>r.some(i=>!!i(s)),"matches");return o("/unrelated-ai-access-collision-probe")?!1:t.some(s=>o(`${s}token`)||o(`${s}mcp`))}a(dg,"middlewareHits");function Hi(e,t=["/.well-known/oauth-","/mcp/oauth/"]){let n=[];for(let r of ig(e)){if(r.handle?.oauthOwned)continue;let o=ag(r);for(let s of o)t.some(i=>Ui(s,i))&&n.push({path:s,source:"route",layer:r.name??"route"});!o.length&&dg(r,t)&&n.push({path:t.join(", "),source:"middleware",layer:r.name??"route"}),r.handle?.stack&&n.push(...Hi({router:{stack:r.handle.stack}},t))}return n}a(Hi,"detectOAuthRouteCollisionDetails");var is=class{constructor(t,n,r,o,s=()=>0){this.clientsStore=t;this.resource=n;this.accessTtl=r;this.refreshTtl=o;this.refreshMax=s}static{a(this,"LocalOAuthProvider")}skipLocalPkceValidation=!1;inflight=new Map;async authorize(t,n,r){throw new Q.InvalidGrantError("Admin consent is required")}async challengeForAuthorizationCode(t,n){let r=this.findCode(t,n);return this.clientsStore.data.codes=this.clientsStore.data.codes.filter(o=>o!==r),this.clientsStore.persist(),this.inflight.set(r.hash,r),setTimeout(()=>this.inflight.delete(r.hash),3e4).unref?.(),r.challenge}async exchangeAuthorizationCode(t,n,r,o,s){let i=this.inflight.get(Lt(n))??this.findCode(t,n);if(this.inflight.delete(i.hash),this.clientsStore.data.codes=this.clientsStore.data.codes.filter(d=>d!==i),this.clientsStore.persist(),i.clientId!==t.client_id||i.expires<=Date.now()||o!==i.redirectUri||s&&Nt(s.href)!==i.resource)throw new Q.InvalidGrantError("Invalid authorization code");return this.issue(t.client_id,i.scopes,i.resource,i.user)}async exchangeRefreshToken(t,n,r,o){let s=this.clientsStore.data.tokens.find(l=>l.hash===Lt(n)&&l.type==="refresh"&&l.clientId===t.client_id);if(!s||s.expires<=Date.now())throw new Q.InvalidGrantError("Invalid refresh token");if(s.rotated)throw this.clientsStore.data.tokens=this.clientsStore.data.tokens.filter(l=>l.family!==s.family),this.clientsStore.persist(),new Q.InvalidGrantError("Refresh token reuse detected");let i=this.refreshMax()*1e3,d=s.familyStart??Date.now();if(i>0&&d+i<=Date.now())throw new Q.InvalidGrantError("Refresh token family reached its maximum lifetime");if(o&&Nt(o.href)!==s.resource)throw new Q.InvalidGrantError("Resource mismatch");if(r?.some(l=>!s.scopes.includes(l)))throw new Q.InvalidGrantError("Invalid scope");return s.rotated=!0,this.issue(t.client_id,r??s.scopes,s.resource,s.user,s.family,d)}async verifyAccessToken(t){let n=this.clientsStore.data.tokens.find(o=>o.hash===Lt(t)&&o.type==="access"&&o.expires>Date.now());if(!n||n.resource!==Nt(this.resource()))throw new Q.InvalidTokenError("Invalid access token");let r=this.clientsStore.data.clients.find(o=>o.client_id===n.clientId);if(!r)throw new Q.InvalidTokenError("Invalid client");return r.lastUsedAt=Date.now(),this.clientsStore.touch(),{token:t,clientId:n.clientId,scopes:n.scopes,expiresAt:Math.floor(n.expires/1e3),resource:new URL(n.resource),extra:{user:n.user}}}async revokeToken(t,n){let r=this.clientsStore.data.tokens.find(o=>o.hash===Lt(n.token)&&o.clientId===t.client_id);r&&(this.clientsStore.data.tokens=this.clientsStore.data.tokens.filter(o=>o.family!==r.family)),this.clientsStore.persist()}createCode(t,n,r){let o=ln();return this.clientsStore.data.codes.push({hash:Lt(o),clientId:t.client_id,challenge:n.codeChallenge,redirectUri:n.redirectUri,scopes:n.scopes??["read"],resource:this.validateResource(n.resource),expires:Date.now()+6e4,user:r}),this.clientsStore.persist(),o}validateResource(t){let n=Nt(this.resource());if(t&&Nt(t.href)!==n)throw new Q.InvalidGrantError("Resource mismatch");return n}findCode(t,n){let r=this.clientsStore.data.codes.find(o=>o.hash===Lt(n)&&o.clientId===t.client_id);if(!r||r.expires<=Date.now())throw new Q.InvalidGrantError("Invalid or expired authorization code");return r}issue(t,n,r,o,s=ln(),i=Date.now()){let d=ln(),l=ln(),c=Date.now(),u=this.refreshMax()*1e3,p=Math.min(c+this.refreshTtl()*1e3,u>0?i+u:1/0),f=this.clientsStore.data,h=f.clients.find(m=>m.client_id===t);return h&&(h.lastUsedAt=c),f.tokens.push({hash:Lt(d),type:"access",clientId:t,family:s,scopes:n,resource:r,expires:c+this.accessTtl()*1e3,user:o,rotated:!1},{hash:Lt(l),type:"refresh",clientId:t,family:s,scopes:n,resource:r,expires:p,user:o,rotated:!1,familyStart:i}),this.clientsStore.persist(),{access_token:d,token_type:"Bearer",expires_in:this.accessTtl(),refresh_token:l,scope:n.join(" ")}}};function Nt(e){let t=new URL(e);if(!["http:","https:"].includes(t.protocol)||t.username||t.password||t.search||t.hash)throw new Error("Invalid resource URL");return`${t.protocol}//${t.host}${t.pathname.replace(/\/+$/,"")}`}a(Nt,"canonicalResource");var Dn=["read","propose"];function lg(e){let t=(e??"/").split("/").filter(Boolean).join("/");return t?`/${t}/`:"/"}a(lg,"consentAdminRoot");var cg=new Set(["localhost","127.0.0.1","[::1]"]);function dn(e){return typeof e=="string"?e:""}a(dn,"value");function sc(e,t,n,r){let o=a(T=>`${n().replace(/\/$/,"")}${T}`,"abs"),s=a((T,k,_,E)=>{let v=k.url;k.url="/",T(k,_,S=>{k.url=v,E(S)})},"atRoot"),i=new Map,d=a(()=>{let T=Date.now();for(let[k,_]of i)_.expires<=T&&i.delete(k)},"sweep"),l=a(T=>{d();let k=[...i].filter(([,_])=>_.clientId===T);for(;k.length>=Xm;)i.delete(k.shift()[0]);return i.size<Zm},"reservePending"),c=a((T,k)=>{T.setHeader("Cache-Control","no-store"),T.setHeader("X-Frame-Options","DENY"),T.setHeader("Referrer-Policy","no-referrer"),T.setHeader("X-Content-Type-Options","nosniff"),T.setHeader("Content-Security-Policy",`default-src 'none'; ${k?`script-src 'nonce-${k}'; `:""}style-src 'unsafe-inline'; connect-src 'self'; form-action 'none'; base-uri 'none'; frame-ancestors 'none'`)},"securityHeaders"),u=Vr(e.settings.httpAdminRoot),p=a(T=>{if(!oe(e.settings))return[...Dn];let k=mt(T);return k==="*"?[...Dn]:k==="read"?["read"]:[]},"grantableFor"),f=a(T=>{let k=T.get("sec-fetch-site");if(k&&k!=="same-origin")return!1;let _=T.get("origin");if(!_)return!0;let E=new Set;try{E.add(new URL(n()).origin)}catch{}let v=T.get("host");return v&&(E.add(`http://${v}`),E.add(`https://${v}`)),E.has(_)},"sameOrigin"),h=a((T,k)=>{d();let _=T.query,E=t.clientsStore.getClient(dn(_.client_id));if(!E){k.status(400).send("Unknown OAuth client");return}let v=dn(_.redirect_uri)||(E.redirect_uris.length===1?E.redirect_uris[0]:"");if(!E.redirect_uris.some(ee=>(0,Xl.redirectUriMatches)(v,ee))){k.status(400).send("Unregistered redirect URI");return}if(_.response_type!=="code"||_.code_challenge_method!=="S256"||!/^[A-Za-z0-9_-]{43,128}$/.test(dn(_.code_challenge))){k.status(400).send("Authorization code with PKCE S256 is required");return}let S=[...new Set((dn(_.scope)||"read").split(" ").filter(Boolean))];if(S.some(ee=>!Dn.includes(ee))){k.status(400).send("Unsupported scope");return}let N=dn(_.resource)||r(),w;try{w=Nt(N)}catch{k.status(400).send("Invalid resource");return}if(w!==Nt(r())){k.status(400).send("Resource mismatch");return}if(!l(E.client_id)){k.status(429).send("Too many pending authorization requests; try again later");return}let F=ln();i.set(F,{clientId:E.client_id,redirect:v,challenge:dn(_.code_challenge),scopes:S,resource:N,state:dn(_.state),csrf:ln(),expires:Date.now()+10*6e4});let I=new URL(v).host,C=(0,Ir.randomBytes)(16).toString("base64");c(k,C),k.status(200).type("html").send(Jl({requestId:F,tokenKey:u,adminRoot:lg(e.settings.httpAdminRoot),nonce:C,redirectHost:I,loopbackRedirect:cg.has(new URL(v).hostname),clientName:E.client_name||"Unnamed client",scopes:S,script:Vl}))},"authorizePage"),m=a((T,k,_)=>{c(k),rc.default.json({limit:"4kb"})(T,k,()=>Ie(e,"api")(T,k,()=>{try{d();let E=/^\/mcp\/oauth\/consent\/([A-Za-z0-9_-]+)\/?$/.exec(T.path)?.[1]??"",v=i.get(E),S=v&&t.clientsStore.getClient(v.clientId);if(!v||!S){k.status(404).json({error:"Consent request expired or unknown"});return}let N=oe(e.settings)&&T.user?.username||"owner",w=v.scopes.filter(C=>p(T).includes(C));if(!w.length){k.status(403).json({error:"Your Node-RED permissions do not allow granting the requested scopes",grantable:w});return}if(T.method==="GET"){v.approver=N,k.json({client_name:S.client_name??null,redirect_host:new URL(v.redirect).host,requested:v.scopes,grantable:w,user:N,csrf:v.csrf});return}if(T.method!=="POST"){k.status(405).end();return}let F=T.body??{};if(!f(T)||F.csrf!==v.csrf||v.approver!==N){k.status(403).json({error:"Consent request verification failed"});return}i.delete(E);let I=new URL(v.redirect);if(F.decision!=="allow")I.searchParams.set("error","access_denied");else{try{t.clientsStore.approve(S.client_id)}catch{k.status(409).json({error:"Maximum active OAuth clients reached"});return}let C=t.createCode(S,{redirectUri:v.redirect,codeChallenge:v.challenge,scopes:w,resource:new URL(v.resource)},N);I.searchParams.set("code",C)}v.state&&I.searchParams.set("state",v.state),k.json({redirect:I.href})}catch(E){_(E)}}))},"consentApi"),g=(0,Zl.clientRegistrationHandler)({clientsStore:t.clientsStore,rateLimit:{windowMs:36e5,max:10,standardHeaders:!0,legacyHeaders:!1}}),y=(0,ec.tokenHandler)({provider:t}),A=(0,Ql.revocationHandler)({provider:t}),x,$="";return{server:a((T,k,_)=>{let E=T.path,v=E.replace(/\/+$/,"").toLowerCase();if(v==="/mcp/oauth/authorize"){T.method==="GET"?h(T,k,_):k.status(405).set("Allow","GET").end();return}if(v.startsWith("/mcp/oauth/consent/")){m(T,k,_);return}if(!["/mcp/oauth/token","/mcp/oauth/register","/mcp/oauth/revoke"].includes(v)){_();return}let S=n();if(S.startsWith("https://")||/^http:\/\/(localhost|127\.0\.0\.1)(:|\/|$)/.test(S)){if($!==S)try{x=(0,nc.mcpAuthRouter)({provider:t,issuerUrl:new URL(S),baseUrl:new URL(`${S.replace(/\/$/,"")}/mcp/oauth/`),resourceServerUrl:new URL(r()),scopesSupported:Dn,clientRegistrationOptions:{rateLimit:{windowMs:36e5,max:10}}}),$=S}catch{x=void 0}if(x){let N=T.url;T.url=T.url.slice(10)||"/",x(T,k,w=>{T.url=N,_(w)});return}}if(E==="/mcp/oauth/token"){s(y,T,k,_);return}if(E==="/mcp/oauth/register"){s(g,T,k,_);return}if(E==="/mcp/oauth/revoke"){s(A,T,k,_);return}_()},"server"),metadata:a((T,k,_)=>{let E=T.path;if(E==="/.well-known/oauth-authorization-server"){s((0,Di.metadataHandler)({issuer:n(),authorization_endpoint:o("/mcp/oauth/authorize"),token_endpoint:o("/mcp/oauth/token"),registration_endpoint:o("/mcp/oauth/register"),revocation_endpoint:o("/mcp/oauth/revoke"),response_types_supported:["code"],code_challenge_methods_supported:["S256"],grant_types_supported:["authorization_code","refresh_token"],token_endpoint_auth_methods_supported:["none"],scopes_supported:Dn}),T,k,_);return}if(E==="/.well-known/oauth-protected-resource/mcp"||E==="/.well-known/oauth-protected-resource"){s((0,Di.metadataHandler)({resource:Nt(r()),authorization_servers:[n()],scopes_supported:Dn}),T,k,_);return}_()},"metadata"),auth:a((T,k,_)=>{try{let E=k.set.bind(k);k.set=((v,S)=>(v==="WWW-Authenticate"&&typeof S=="string"&&(S=S.replace('scope="read"','scope="read propose"')),typeof v=="string"?E(v,S??""):E(v))),(0,tc.requireBearerAuth)({verifier:t,requiredScopes:["read"],resourceMetadataUrl:o(e.settings.httpNodeRoot===!1||!e.httpNode?"/mcp/.well-known/oauth-protected-resource/mcp":"/.well-known/oauth-protected-resource/mcp"),expectedResource:new URL(r())})(T,k,v=>{k.set=E,_(v)})}catch(E){_(E)}},"auth")}}a(sc,"makeOAuthHandlers");var Lr=require("fs"),jn=require("path"),ic=require("url"),ac=require("yaml"),hg={};function ug(){return typeof __dirname<"u"?__dirname:(0,jn.dirname)((0,ic.fileURLToPath)(hg.url))}a(ug,"moduleDir");function dc(){let e=ug(),t=[(0,jn.join)(e,"..","docs","api","openapi.yaml"),(0,jn.join)(e,"..","..","docs","api","openapi.yaml"),(0,jn.join)(process.cwd(),"docs","api","openapi.yaml")];for(let n of t)if((0,Lr.existsSync)(n))return n;return null}a(dc,"resolveOpenApiYamlPath");function pg(){let e=dc();if(!e)throw new Error("OpenAPI specification file was not found");return(0,Lr.readFileSync)(e,"utf8")}a(pg,"loadOpenApiYaml");function lc(){return(e,t)=>{try{let n=pg();t.status(200).type("application/yaml").send(n)}catch(n){let r=n instanceof Error?n.message:String(n);t.status(500).json({error:r})}}}a(lc,"openApiYamlHandler");var Bi;function fg(){let e=dc();if(!e)throw new Error("OpenAPI specification file was not found");return Bi?.path!==e&&(Bi={path:e,json:JSON.stringify((0,ac.parse)((0,Lr.readFileSync)(e,"utf8")))}),Bi.json}a(fg,"loadOpenApiJson");function cc(){return(e,t)=>{try{t.status(200).type("application/json").send(fg())}catch(n){let r=n instanceof Error?n.message:String(n);t.status(500).json({error:r})}}}a(cc,"openApiJsonHandler");function mg(){return`
|
|
1177
|
+
})();`;Te();var Ut=a(e=>(0,Ur.createHash)("sha256").update(e).digest("hex"),"hash"),wn=a(()=>(0,Ur.randomBytes)(32).toString("base64url"),"secret"),hg=36e5,mg=25,gg=5,yg=500,mc=a(()=>({clients:[],codes:[],tokens:[]}),"emptyDb"),gs=class{constructor(t,n){this.maxClients=n;this.path=(0,ta.join)(t.root,"oauth.json");try{let r=(0,Qn.existsSync)(this.path)?JSON.parse((0,Qn.readFileSync)(this.path,"utf8")):mc();if(!r||typeof r!="object"||!("clients"in r)||!("codes"in r)||!("tokens"in r)||!Array.isArray(r.clients)||!Array.isArray(r.codes)||!Array.isArray(r.tokens))throw new Error("Invalid OAuth store");this.db=r}catch{this.db=mc()}(0,Qn.mkdirSync)(t.root,{recursive:!0,mode:448}),this.prune()}static{a(this,"OAuthStore")}db;path;save(){bn((0,ta.join)(this.path,".."),this.path,St(this.db,8e6,{kind:"oauth",pretty:!0}))}prune(t=Date.now()){let n=this.db.clients.length+this.db.codes.length+this.db.tokens.length,r=new Set(this.db.clients.filter(o=>o.approved?(o.lastUsedAt??(o.client_id_issued_at??0)*1e3)>t-30*864e5:(o.client_id_issued_at??0)*1e3>t-hg).map(o=>o.client_id));this.db.clients=this.db.clients.filter(o=>r.has(o.client_id)),this.db.codes=this.db.codes.filter(o=>o.expires>t&&r.has(o.clientId)),this.db.tokens=this.db.tokens.filter(o=>r.has(o.clientId)&&o.expires>t),this.db.clients.length+this.db.codes.length+this.db.tokens.length!==n&&this.save()}getClient(t){return this.prune(),this.db.clients.find(n=>n.client_id===t)}registerClient(t){if(this.prune(),this.approvedCount()>=this.maxClients())throw new ne.InvalidClientMetadataError("Maximum active OAuth clients reached");if(this.db.clients.length-this.approvedCount()>=mg)throw new ne.InvalidClientMetadataError("Too many OAuth clients awaiting consent");if(!Array.isArray(t.redirect_uris)||!t.redirect_uris.length||t.redirect_uris.some(r=>{try{let o=new URL(r);return!!o.username||!!o.password||!!o.hash||o.protocol!=="https:"&&!(o.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(o.hostname))}catch{return!0}}))throw new ne.InvalidClientMetadataError("Redirect URIs must use HTTPS or loopback HTTP");if(t.token_endpoint_auth_method&&t.token_endpoint_auth_method!=="none")throw new ne.InvalidClientMetadataError("Only public clients are supported");let n={...t,client_secret:void 0,client_secret_expires_at:void 0,client_id:wn(),client_id_issued_at:Math.floor(Date.now()/1e3),token_endpoint_auth_method:"none",lastUsedAt:Date.now()};return this.db.clients.push(n),this.save(),n}approvedCount(){return this.db.clients.filter(t=>t.approved).length}approve(t){let n=this.db.clients.find(r=>r.client_id===t);if(!(!n||n.approved)){if(this.approvedCount()>=this.maxClients())throw new ne.InvalidClientMetadataError("Maximum active OAuth clients reached");n.approved=!0,this.save()}}get data(){return this.db}persist(){this.touchTimer&&(clearTimeout(this.touchTimer),this.touchTimer=void 0),this.save()}touchTimer;touch(){this.touchTimer||(this.touchTimer=setTimeout(()=>{this.touchTimer=void 0,this.save()},5e3),this.touchTimer.unref?.())}revokeClient(t){let n=this.db.clients.length;return this.db.clients=this.db.clients.filter(r=>r.client_id!==t),this.db.tokens=this.db.tokens.filter(r=>r.clientId!==t),this.db.codes=this.db.codes.filter(r=>r.clientId!==t),this.save(),this.db.clients.length!==n}};function oa(e){switch(e.source){case"http-in":return`Route ${e.path} is already used by http-in node '${e.nodeName??e.nodeId??"unnamed"}'`;case"httpStatic":return`File ${e.path} is served by httpStatic`;case"middleware":return`Path prefix is already handled by mounted middleware (${e.layer??"unknown"})`;default:return`Route ${e.path} is already registered by another handler (${e.layer??"route"})`}}a(oa,"describeOAuthRouteCollision");var sa=a((e,t)=>e.toLowerCase().startsWith(t.toLowerCase()),"startsWithCi");function Qi(e){return e===!1?void 0:`/${typeof e=="string"?e:"/"}`.replace(/\/+/g,"/").replace(/\/$/,"")}a(Qi,"normalizeRoot");function ea(e,t){if(e!==void 0){if(!e)return t;if(sa(t,`${e}/`))return t.slice(e.length)}}a(ea,"relativePrefix");function wg(e){let t=new Map;for(let r of e)r.id&&t.set(r.id,r);let n=a((r,o=new Set)=>{if(!r)return!0;let s=t.get(r);return s?s.type==="tab"?!s.disabled&&!s.d:s.type!=="subflow"||o.has(r)?!1:(o.add(r),e.some(i=>i.type===`subflow:${r}`&&!i.d&&n(i.z,o))):!0},"live");return n}a(wg,"makeLiveCheck");function bg(e,t){if(!e||!t.length)return[];let n=[];try{e(s=>n.push(s))}catch{return[]}let r=wg(n),o=[];for(let s of n){if(s.type!=="http in"||typeof s.url!="string"||s.d)continue;let i=s.url.startsWith("/")?s.url:`/${s.url}`;!t.some(d=>sa(i,d))||!r(s.z)||o.push({path:i,source:"http-in",nodeId:s.id,nodeName:s.name?s.name:void 0,flowId:s.z})}return o}a(bg,"findHttpInCollisions");var vg=new Set(["bound dispatch","handle"]),kg=["authorize","token","register","revoke"],Ag=["authorization-server","protected-resource"];function xg(e){let t=e.length;for(;t>0&&e.charCodeAt(t-1)===47;)t--;return e.slice(0,t)}a(xg,"trimTrailingSlashes");function Sg(e){let t=new Set;return e.filter(n=>{let r=`${n.source}\0${n.path.toLowerCase()}`;return t.has(r)?!1:(t.add(r),!0)})}a(Sg,"dedupe");function xc(e){let t=Qi(e.httpAdminRoot),n=e.httpNode?Qi(e.httpNodeRoot):void 0,r=`${t??""}/mcp/oauth/`,o=e.metadataFallback?`${t??""}/mcp/.well-known/oauth-`:`${n??""}/.well-known/oauth-`,s=a(p=>{let f=ea(t,p),h=ea(n,p),m=h?bg(e.eachNode,[h]):[],g=new Set(m.map(k=>k.path.toLowerCase())),w=h?ra(e.httpNode,[h]).filter(k=>!(k.source==="route"&&vg.has(k.layer??"")&&g.has(k.path.toLowerCase()))):[],v=f?ra(e.httpAdmin,[f]):[];return Sg([...v,...w,...m])},"scan"),i=s(r),d=s(o),l=e.fileExists??(()=>!1),c=(e.staticDirs??[]).map(p=>typeof p=="string"?{path:p,root:e.httpStaticRoot}:p),u=a((p,f)=>{for(let h of c){let m=Qi(h.root),g=xg(h.path);for(let w of p){let v=ea(m,w);!v||!l(`${g}${v}`)||f.push({path:w,source:"httpStatic",file:`${g}${v}`})}}},"checkStatic");return u(kg.map(p=>`${r}${p}`),i),u(Ag.map(p=>`${o}${p}`),d),{authorization:i,metadata:d}}a(xc,"scanOAuthRouteCollisions");function Rg(e){let t=a(n=>{try{return e?.[n]}catch{return}},"read");return t("_router")?.stack??t("router")?.stack??[]}a(Rg,"routerStack");function Tg(e){let t=e.route?.path;return t?(Array.isArray(t)?t:[t]).map(String):[]}a(Tg,"layerPaths");function Pg(e,t){if(e.name==="router")return!1;let n=e.regexp,r=e.matchers??(n?[s=>n.test(s)]:[]);if(!r.length)return!1;let o=a(s=>r.some(i=>!!i(s)),"matches");return o("/unrelated-ai-access-collision-probe")?!1:t.some(s=>o(`${s}token`)||o(`${s}mcp`))}a(Pg,"middlewareHits");function ra(e,t=["/.well-known/oauth-","/mcp/oauth/"]){let n=[];for(let r of Rg(e)){if(r.handle?.oauthOwned)continue;let o=Tg(r);for(let s of o)t.some(i=>sa(s,i))&&n.push({path:s,source:"route",layer:r.name??"route"});!o.length&&Pg(r,t)&&n.push({path:t.join(", "),source:"middleware",layer:r.name??"route"}),r.handle?.stack&&n.push(...ra({router:{stack:r.handle.stack}},t))}return n}a(ra,"detectOAuthRouteCollisionDetails");var ys=class{constructor(t,n,r,o,s=()=>0){this.clientsStore=t;this.resource=n;this.accessTtl=r;this.refreshTtl=o;this.refreshMax=s}static{a(this,"LocalOAuthProvider")}skipLocalPkceValidation=!1;inflight=new Map;async authorize(t,n,r){throw new ne.InvalidGrantError("Admin consent is required")}async challengeForAuthorizationCode(t,n){let r=this.findCode(t,n);return this.clientsStore.data.codes=this.clientsStore.data.codes.filter(o=>o!==r),this.clientsStore.persist(),this.inflight.set(r.hash,r),setTimeout(()=>this.inflight.delete(r.hash),3e4).unref?.(),r.challenge}async exchangeAuthorizationCode(t,n,r,o,s){let i=this.inflight.get(Ut(n))??this.findCode(t,n);if(this.inflight.delete(i.hash),this.clientsStore.data.codes=this.clientsStore.data.codes.filter(d=>d!==i),this.clientsStore.persist(),i.clientId!==t.client_id||i.expires<=Date.now()||o!==i.redirectUri||s&&Bt(s.href)!==i.resource)throw new ne.InvalidGrantError("Invalid authorization code");return this.issue(t.client_id,i.scopes,i.resource,i.user)}async exchangeRefreshToken(t,n,r,o){let s=this.clientsStore.data.tokens.find(l=>l.hash===Ut(n)&&l.type==="refresh"&&l.clientId===t.client_id);if(!s||s.expires<=Date.now())throw new ne.InvalidGrantError("Invalid refresh token");if(s.rotated)throw this.clientsStore.data.tokens=this.clientsStore.data.tokens.filter(l=>l.family!==s.family),this.clientsStore.persist(),new ne.InvalidGrantError("Refresh token reuse detected");let i=this.refreshMax()*1e3,d=s.familyStart??Date.now();if(i>0&&d+i<=Date.now())throw new ne.InvalidGrantError("Refresh token family reached its maximum lifetime");if(o&&Bt(o.href)!==s.resource)throw new ne.InvalidGrantError("Resource mismatch");if(r?.some(l=>!s.scopes.includes(l)))throw new ne.InvalidGrantError("Invalid scope");return s.rotated=!0,this.issue(t.client_id,r??s.scopes,s.resource,s.user,s.family,d)}async verifyAccessToken(t){let n=this.clientsStore.data.tokens.find(o=>o.hash===Ut(t)&&o.type==="access"&&o.expires>Date.now());if(!n||n.resource!==Bt(this.resource()))throw new ne.InvalidTokenError("Invalid access token");let r=this.clientsStore.data.clients.find(o=>o.client_id===n.clientId);if(!r)throw new ne.InvalidTokenError("Invalid client");return r.lastUsedAt=Date.now(),this.clientsStore.touch(),{token:t,clientId:n.clientId,scopes:n.scopes,expiresAt:Math.floor(n.expires/1e3),resource:new URL(n.resource),extra:{user:n.user}}}async revokeToken(t,n){let r=this.clientsStore.data.tokens.find(o=>o.hash===Ut(n.token)&&o.clientId===t.client_id);r&&(this.clientsStore.data.tokens=this.clientsStore.data.tokens.filter(o=>o.family!==r.family)),this.clientsStore.persist()}createCode(t,n,r){let o=wn();return this.clientsStore.data.codes.push({hash:Ut(o),clientId:t.client_id,challenge:n.codeChallenge,redirectUri:n.redirectUri,scopes:n.scopes??["read"],resource:this.validateResource(n.resource),expires:Date.now()+6e4,user:r}),this.clientsStore.persist(),o}validateResource(t){let n=Bt(this.resource());if(t&&Bt(t.href)!==n)throw new ne.InvalidGrantError("Resource mismatch");return n}findCode(t,n){let r=this.clientsStore.data.codes.find(o=>o.hash===Ut(n)&&o.clientId===t.client_id);if(!r||r.expires<=Date.now())throw new ne.InvalidGrantError("Invalid or expired authorization code");return r}issue(t,n,r,o,s=wn(),i=Date.now()){let d=wn(),l=wn(),c=Date.now(),u=this.refreshMax()*1e3,p=Math.min(c+this.refreshTtl()*1e3,u>0?i+u:1/0),f=this.clientsStore.data,h=f.clients.find(m=>m.client_id===t);return h&&(h.lastUsedAt=c),f.tokens.push({hash:Ut(d),type:"access",clientId:t,family:s,scopes:n,resource:r,expires:c+this.accessTtl()*1e3,user:o,rotated:!1},{hash:Ut(l),type:"refresh",clientId:t,family:s,scopes:n,resource:r,expires:p,user:o,rotated:!1,familyStart:i}),this.clientsStore.persist(),{access_token:d,token_type:"Bearer",expires_in:this.accessTtl(),refresh_token:l,scope:n.join(" ")}}};function Bt(e){let t=new URL(e);if(!["http:","https:"].includes(t.protocol)||t.username||t.password||t.search||t.hash)throw new Error("Invalid resource URL");return`${t.protocol}//${t.host}${t.pathname.replace(/\/+$/,"")}`}a(Bt,"canonicalResource");var Xn=["read","propose"];function _g(e){let t=(e??"/").split("/").filter(Boolean).join("/");return t?`/${t}/`:"/"}a(_g,"consentAdminRoot");var Cg=new Set(["localhost","127.0.0.1","[::1]"]);function yn(e){return typeof e=="string"?e:""}a(yn,"value");function Sc(e,t,n,r){let o=a(T=>`${n().replace(/\/$/,"")}${T}`,"abs"),s=a((T,S,F,N)=>{let P=S.url;S.url="/",T(S,F,A=>{S.url=P,N(A)})},"atRoot"),i=new Map,d=a(()=>{let T=Date.now();for(let[S,F]of i)F.expires<=T&&i.delete(S)},"sweep"),l=a(T=>{d();let S=[...i].filter(([,F])=>F.clientId===T);for(;S.length>=gg;)i.delete(S.shift()[0]);return i.size<yg},"reservePending"),c=a((T,S)=>{T.setHeader("Cache-Control","no-store"),T.setHeader("X-Frame-Options","DENY"),T.setHeader("Referrer-Policy","no-referrer"),T.setHeader("X-Content-Type-Options","nosniff"),T.setHeader("Content-Security-Policy",`default-src 'none'; ${S?`script-src 'nonce-${S}'; `:""}style-src 'unsafe-inline'; connect-src 'self'; form-action 'none'; base-uri 'none'; frame-ancestors 'none'`)},"securityHeaders"),u=so(e.settings.httpAdminRoot),p=a(T=>{if(!ae(e.settings))return[...Xn];let S=Tt(T);return S==="*"?[...Xn]:S==="read"?["read"]:[]},"grantableFor"),f=a(T=>{let S=T.get("sec-fetch-site");if(S&&S!=="same-origin")return!1;let F=T.get("origin");if(!F)return!0;let N=new Set;try{N.add(new URL(n()).origin)}catch{}let P=T.get("host");return P&&(N.add(`http://${P}`),N.add(`https://${P}`)),N.has(F)},"sameOrigin"),h=a((T,S)=>{d();let F=T.query,N=t.clientsStore.getClient(yn(F.client_id));if(!N){S.status(400).send("Unknown OAuth client");return}let P=yn(F.redirect_uri)||(N.redirect_uris.length===1?N.redirect_uris[0]:"");if(!N.redirect_uris.some(ee=>(0,gc.redirectUriMatches)(P,ee))){S.status(400).send("Unregistered redirect URI");return}if(F.response_type!=="code"||F.code_challenge_method!=="S256"||!/^[A-Za-z0-9_-]{43,128}$/.test(yn(F.code_challenge))){S.status(400).send("Authorization code with PKCE S256 is required");return}let A=[...new Set((yn(F.scope)||"read").split(" ").filter(Boolean))];if(A.some(ee=>!Xn.includes(ee))){S.status(400).send("Unsupported scope");return}let I=yn(F.resource)||r(),b;try{b=Bt(I)}catch{S.status(400).send("Invalid resource");return}if(b!==Bt(r())){S.status(400).send("Resource mismatch");return}if(!l(N.client_id)){S.status(429).send("Too many pending authorization requests; try again later");return}let M=wn();i.set(M,{clientId:N.client_id,redirect:P,challenge:yn(F.code_challenge),scopes:A,resource:I,state:yn(F.state),csrf:wn(),expires:Date.now()+10*6e4});let H=new URL(P).host,L=(0,Ur.randomBytes)(16).toString("base64");c(S,L),S.status(200).type("html").send(fc({requestId:M,tokenKey:u,adminRoot:_g(e.settings.httpAdminRoot),nonce:L,redirectHost:H,loopbackRedirect:Cg.has(new URL(P).hostname),clientName:N.client_name||"Unnamed client",scopes:A,script:hc}))},"authorizePage"),m=a((T,S,F)=>{c(S),Ac.default.json({limit:"4kb"})(T,S,()=>Ce(e,"api")(T,S,()=>{try{d();let N=/^\/mcp\/oauth\/consent\/([A-Za-z0-9_-]+)\/?$/.exec(T.path)?.[1]??"",P=i.get(N),A=P&&t.clientsStore.getClient(P.clientId);if(!P||!A){S.status(404).json({error:"Consent request expired or unknown"});return}let I=ae(e.settings)&&T.user?.username||"owner",b=P.scopes.filter(L=>p(T).includes(L));if(!b.length){S.status(403).json({error:"Your Node-RED permissions do not allow granting the requested scopes",grantable:b});return}if(T.method==="GET"){P.approver=I,S.json({client_name:A.client_name??null,redirect_host:new URL(P.redirect).host,requested:P.scopes,grantable:b,user:I,csrf:P.csrf});return}if(T.method!=="POST"){S.status(405).end();return}let M=T.body??{};if(!f(T)||M.csrf!==P.csrf||P.approver!==I){S.status(403).json({error:"Consent request verification failed"});return}i.delete(N);let H=new URL(P.redirect);if(M.decision!=="allow")H.searchParams.set("error","access_denied");else{try{t.clientsStore.approve(A.client_id)}catch{S.status(409).json({error:"Maximum active OAuth clients reached"});return}let L=t.createCode(A,{redirectUri:P.redirect,codeChallenge:P.challenge,scopes:b,resource:new URL(P.resource)},I);H.searchParams.set("code",L)}P.state&&H.searchParams.set("state",P.state),S.json({redirect:H.href})}catch(N){F(N)}}))},"consentApi"),g=(0,yc.clientRegistrationHandler)({clientsStore:t.clientsStore,rateLimit:{windowMs:36e5,max:10,standardHeaders:!0,legacyHeaders:!1}}),w=(0,bc.tokenHandler)({provider:t}),v=(0,wc.revocationHandler)({provider:t}),k,$="";return{server:a((T,S,F)=>{let N=T.path,P=N.replace(/\/+$/,"").toLowerCase();if(P==="/mcp/oauth/authorize"){T.method==="GET"?h(T,S,F):S.status(405).set("Allow","GET").end();return}if(P.startsWith("/mcp/oauth/consent/")){m(T,S,F);return}if(!["/mcp/oauth/token","/mcp/oauth/register","/mcp/oauth/revoke"].includes(P)){F();return}let A=n();if(A.startsWith("https://")||/^http:\/\/(localhost|127\.0\.0\.1)(:|\/|$)/.test(A)){if($!==A)try{k=(0,kc.mcpAuthRouter)({provider:t,issuerUrl:new URL(A),baseUrl:new URL(`${A.replace(/\/$/,"")}/mcp/oauth/`),resourceServerUrl:new URL(r()),scopesSupported:Xn,clientRegistrationOptions:{rateLimit:{windowMs:36e5,max:10}}}),$=A}catch{k=void 0}if(k){let I=T.url;T.url=T.url.slice(10)||"/",k(T,S,b=>{T.url=I,F(b)});return}}if(N==="/mcp/oauth/token"){s(w,T,S,F);return}if(N==="/mcp/oauth/register"){s(g,T,S,F);return}if(N==="/mcp/oauth/revoke"){s(v,T,S,F);return}F()},"server"),metadata:a((T,S,F)=>{let N=T.path;if(N==="/.well-known/oauth-authorization-server"){s((0,na.metadataHandler)({issuer:n(),authorization_endpoint:o("/mcp/oauth/authorize"),token_endpoint:o("/mcp/oauth/token"),registration_endpoint:o("/mcp/oauth/register"),revocation_endpoint:o("/mcp/oauth/revoke"),response_types_supported:["code"],code_challenge_methods_supported:["S256"],grant_types_supported:["authorization_code","refresh_token"],token_endpoint_auth_methods_supported:["none"],scopes_supported:Xn}),T,S,F);return}if(N==="/.well-known/oauth-protected-resource/mcp"||N==="/.well-known/oauth-protected-resource"){s((0,na.metadataHandler)({resource:Bt(r()),authorization_servers:[n()],scopes_supported:Xn}),T,S,F);return}F()},"metadata"),auth:a((T,S,F)=>{try{let N=S.set.bind(S);S.set=((P,A)=>(P==="WWW-Authenticate"&&typeof A=="string"&&(A=A.replace('scope="read"','scope="read propose"')),typeof P=="string"?N(P,A??""):N(P))),(0,vc.requireBearerAuth)({verifier:t,requiredScopes:["read"],resourceMetadataUrl:o(e.settings.httpNodeRoot===!1||!e.httpNode?"/mcp/.well-known/oauth-protected-resource/mcp":"/.well-known/oauth-protected-resource/mcp"),expectedResource:new URL(r())})(T,S,P=>{S.set=N,F(P)})}catch(N){F(N)}},"auth")}}a(Sc,"makeOAuthHandlers");y();var Br=require("fs"),er=require("path"),Rc=require("url"),Tc=require("yaml");function Eg(){return typeof __dirname<"u"?__dirname:(0,er.dirname)((0,Rc.fileURLToPath)(R))}a(Eg,"moduleDir");function Pc(){let e=Eg(),t=[(0,er.join)(e,"..","docs","api","openapi.yaml"),(0,er.join)(e,"..","..","docs","api","openapi.yaml"),(0,er.join)(process.cwd(),"docs","api","openapi.yaml")];for(let n of t)if((0,Br.existsSync)(n))return n;return null}a(Pc,"resolveOpenApiYamlPath");function Ig(){let e=Pc();if(!e)throw new Error("OpenAPI specification file was not found");return(0,Br.readFileSync)(e,"utf8")}a(Ig,"loadOpenApiYaml");function _c(){return(e,t)=>{try{let n=Ig();t.status(200).type("application/yaml").send(n)}catch(n){let r=n instanceof Error?n.message:String(n);t.status(500).json({error:r})}}}a(_c,"openApiYamlHandler");var ia;function Lg(){let e=Pc();if(!e)throw new Error("OpenAPI specification file was not found");return ia?.path!==e&&(ia={path:e,json:JSON.stringify((0,Tc.parse)((0,Br.readFileSync)(e,"utf8")))}),ia.json}a(Lg,"loadOpenApiJson");function Cc(){return(e,t)=>{try{t.status(200).type("application/json").send(Lg())}catch(n){let r=n instanceof Error?n.message:String(n);t.status(500).json({error:r})}}}a(Cc,"openApiJsonHandler");y();y();y();y();function Ng(){return`
|
|
1178
1178
|
var settingsData = null;
|
|
1179
1179
|
var settingsSaving = false;
|
|
1180
1180
|
var settingsTimer = null;
|
|
@@ -1285,9 +1285,9 @@ ${os}
|
|
|
1285
1285
|
var payload = {
|
|
1286
1286
|
enabled: $('set-enabled').checked,
|
|
1287
1287
|
allowWithoutAdminAuth: $('set-allow-without').checked,
|
|
1288
|
-
defaultLinkTtlSeconds:
|
|
1288
|
+
defaultLinkTtlSeconds: Number($('set-ttl').value) * 3600,
|
|
1289
1289
|
defaultDeploymentType: $('set-deploy').value,
|
|
1290
|
-
proposalTtlDays:
|
|
1290
|
+
proposalTtlDays: Number($('set-proposal-ttl').value),
|
|
1291
1291
|
maxActiveLinks: Number($('set-max-links').value),
|
|
1292
1292
|
publicBaseUrl: String($('set-public-url').value).trim(),
|
|
1293
1293
|
corsAllowedOrigins: String($('set-cors').value).split(',').map(function (x) { return x.trim(); }).filter(Boolean),
|
|
@@ -1312,7 +1312,7 @@ ${os}
|
|
|
1312
1312
|
return payload;
|
|
1313
1313
|
}
|
|
1314
1314
|
|
|
1315
|
-
`}a(
|
|
1315
|
+
`}a(Ng,"settingsPartForm");function Fg(){return`
|
|
1316
1316
|
function secretPatternsEditable() {
|
|
1317
1317
|
return settingsCanEdit() && !fieldLocked('secretNamePatterns');
|
|
1318
1318
|
}
|
|
@@ -1400,9 +1400,9 @@ ${os}
|
|
|
1400
1400
|
});
|
|
1401
1401
|
}
|
|
1402
1402
|
|
|
1403
|
-
`}a(
|
|
1403
|
+
`}a(Fg,"settingsPartSecrets");function Og(){return`
|
|
1404
1404
|
function settingsFlash(msg, ok) {
|
|
1405
|
-
var tab = document.querySelector('nav.tabs button[aria-
|
|
1405
|
+
var tab = document.querySelector('nav.tabs button[aria-current="page"]');
|
|
1406
1406
|
var target = tab && tab.getAttribute('data-tab') === 'oauth' ? 'oauth-flash' : 'settings-flash';
|
|
1407
1407
|
if (ok) showToast(msg, true);
|
|
1408
1408
|
else showFlash($(target), msg, false);
|
|
@@ -1423,7 +1423,6 @@ ${os}
|
|
|
1423
1423
|
loadOAuthStatus();
|
|
1424
1424
|
} catch (e) {
|
|
1425
1425
|
settingsFlash(e.message || String(e), false);
|
|
1426
|
-
loadSettings();
|
|
1427
1426
|
} finally {
|
|
1428
1427
|
settingsSaving = false;
|
|
1429
1428
|
}
|
|
@@ -1431,6 +1430,20 @@ ${os}
|
|
|
1431
1430
|
|
|
1432
1431
|
function saveSettingsNow() {
|
|
1433
1432
|
if (!settingsCanEdit() || settingsSaving) return;
|
|
1433
|
+
var fields = ['set-ttl', 'set-proposal-ttl', 'set-max-links', 'set-rate-window', 'set-rate-ip', 'set-rate-token',
|
|
1434
|
+
'oauth-access-ttl', 'oauth-refresh-ttl', 'oauth-refresh-max', 'oauth-dcr-cap'];
|
|
1435
|
+
for (var i = 0; i < fields.length; i++) {
|
|
1436
|
+
var input = $(fields[i]);
|
|
1437
|
+
if (!input || input.disabled) continue;
|
|
1438
|
+
var n = Number(input.value), min = Number(input.min || 0);
|
|
1439
|
+
var max = input.max ? Number(input.max) : Infinity;
|
|
1440
|
+
if (!input.value.trim() || !Number.isInteger(n) || n < min || n > max) {
|
|
1441
|
+
input.setAttribute('aria-invalid', 'true');
|
|
1442
|
+
settingsFlash('Enter a whole number with a minimum of ' + min + (max < Infinity ? ' and a maximum of ' + max : '') + '.', false);
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
input.removeAttribute('aria-invalid');
|
|
1446
|
+
}
|
|
1434
1447
|
var payload = collectSettingsPayload();
|
|
1435
1448
|
var was = settingsData && settingsData.settings && settingsData.settings.oauth && settingsData.settings.oauth.enabled;
|
|
1436
1449
|
if (!was && payload.oauth && payload.oauth.enabled) {
|
|
@@ -1473,6 +1486,7 @@ ${os}
|
|
|
1473
1486
|
el.className = 'check-result' + (state ? ' is-' + state : '');
|
|
1474
1487
|
el.textContent = text || '';
|
|
1475
1488
|
el.hidden = !text;
|
|
1489
|
+
el.setAttribute('role', state === 'fail' ? 'alert' : 'status');
|
|
1476
1490
|
}
|
|
1477
1491
|
|
|
1478
1492
|
async function checkPublicUrl() {
|
|
@@ -1488,7 +1502,7 @@ ${os}
|
|
|
1488
1502
|
}
|
|
1489
1503
|
}
|
|
1490
1504
|
|
|
1491
|
-
`}a(
|
|
1505
|
+
`}a(Og,"settingsPartSave");function $g(){return`
|
|
1492
1506
|
function editorNodeHref(c) {
|
|
1493
1507
|
var base = editorRoot && editorRoot !== '/' ? editorRoot.replace(/\\/$/, '') + '/' : '/';
|
|
1494
1508
|
if (c.nodeId) return base + '#node/' + encodeURIComponent(c.nodeId);
|
|
@@ -1596,7 +1610,7 @@ ${os}
|
|
|
1596
1610
|
}
|
|
1597
1611
|
}
|
|
1598
1612
|
|
|
1599
|
-
`}a(
|
|
1613
|
+
`}a($g,"settingsPartOAuth");function Mg(){return`
|
|
1600
1614
|
function loadOAuthTab() {
|
|
1601
1615
|
loadSettings();
|
|
1602
1616
|
loadOAuthStatus();
|
|
@@ -1629,7 +1643,7 @@ ${os}
|
|
|
1629
1643
|
showToast('Server URL copied.', true);
|
|
1630
1644
|
});
|
|
1631
1645
|
})();
|
|
1632
|
-
`}a(
|
|
1646
|
+
`}a(Mg,"settingsPartWire");function Ec(){return Ng()+Fg()+Og()+$g()+Mg()}a(Ec,"pageClientSettingsScript");function Hg(){return`
|
|
1633
1647
|
(function () {
|
|
1634
1648
|
var root = document.getElementById('mcp-root');
|
|
1635
1649
|
if (!root) return;
|
|
@@ -1647,6 +1661,7 @@ ${os}
|
|
|
1647
1661
|
var proposalsFilter = 'pending';
|
|
1648
1662
|
var proposalsSearchQuery = '';
|
|
1649
1663
|
var cachedProposals = [];
|
|
1664
|
+
var deployUnknownBlocks = [];
|
|
1650
1665
|
// Decided proposals load in pages (Load more); pending always load in full.
|
|
1651
1666
|
var DECIDED_PAGE_SIZE = 50;
|
|
1652
1667
|
var DECIDED_STATUSES = 'approved,rejected,failed,withdrawn,expired,superseded';
|
|
@@ -1713,6 +1728,7 @@ ${os}
|
|
|
1713
1728
|
function showFlash(el, message, ok) {
|
|
1714
1729
|
if (!el) return;
|
|
1715
1730
|
el.className = 'flash ' + (ok ? 'ok' : 'error');
|
|
1731
|
+
el.setAttribute('role', ok ? 'status' : 'alert');
|
|
1716
1732
|
el.textContent = message;
|
|
1717
1733
|
el.hidden = !message;
|
|
1718
1734
|
}
|
|
@@ -1765,6 +1781,7 @@ ${os}
|
|
|
1765
1781
|
if (!st) return;
|
|
1766
1782
|
st.textContent = text;
|
|
1767
1783
|
st.className = 'sg-status' + (isError ? ' error' : '');
|
|
1784
|
+
st.setAttribute('role', isError ? 'alert' : 'status');
|
|
1768
1785
|
st.hidden = !text;
|
|
1769
1786
|
}
|
|
1770
1787
|
function authGateContinue() {
|
|
@@ -1860,7 +1877,7 @@ ${os}
|
|
|
1860
1877
|
}
|
|
1861
1878
|
});
|
|
1862
1879
|
}
|
|
1863
|
-
${
|
|
1880
|
+
${ms}
|
|
1864
1881
|
function isStatusPath(path) {
|
|
1865
1882
|
return path === '/status' || path.indexOf('/status?') === 0;
|
|
1866
1883
|
}
|
|
@@ -1875,6 +1892,7 @@ ${os}
|
|
|
1875
1892
|
opts = opts || {};
|
|
1876
1893
|
var el = document.createElement('div');
|
|
1877
1894
|
el.className = 'toast ' + (ok ? 'ok' : 'error');
|
|
1895
|
+
el.setAttribute('role', ok ? 'status' : 'alert');
|
|
1878
1896
|
if (opts.title) {
|
|
1879
1897
|
var titleEl = document.createElement('span');
|
|
1880
1898
|
titleEl.className = 'toast-title';
|
|
@@ -1948,7 +1966,11 @@ ${os}
|
|
|
1948
1966
|
topic: 'Topic',
|
|
1949
1967
|
payload: 'Payload',
|
|
1950
1968
|
complete: 'Complete',
|
|
1951
|
-
info: '
|
|
1969
|
+
info: 'Description',
|
|
1970
|
+
label: 'Flow name',
|
|
1971
|
+
env: 'Environment variables',
|
|
1972
|
+
disabled: 'Flow disabled',
|
|
1973
|
+
credentials: 'Credentials',
|
|
1952
1974
|
format: 'Format',
|
|
1953
1975
|
syntax: 'Syntax',
|
|
1954
1976
|
outputs: 'Outputs',
|
|
@@ -1993,7 +2015,7 @@ ${os}
|
|
|
1993
2015
|
}
|
|
1994
2016
|
return body;
|
|
1995
2017
|
}
|
|
1996
|
-
`}a(
|
|
2018
|
+
`}a(Hg,"pageClientScriptPartA");function jg(){return`
|
|
1997
2019
|
// Time helpers \u2014 keep in sync with page-time.ts (always browser-local Intl).
|
|
1998
2020
|
function titleFromDisplayName(displayName) {
|
|
1999
2021
|
var s = String(displayName || '');
|
|
@@ -2100,7 +2122,7 @@ ${os}
|
|
|
2100
2122
|
btn.disabled = false;
|
|
2101
2123
|
}, 1600);
|
|
2102
2124
|
}
|
|
2103
|
-
`}a(
|
|
2125
|
+
`}a(jg,"pageClientScriptPartATimeCopy");function Ug(){return`
|
|
2104
2126
|
function shortId(value, keep) {
|
|
2105
2127
|
var s = String(value == null ? '' : value);
|
|
2106
2128
|
var n = keep || 8;
|
|
@@ -2170,6 +2192,7 @@ ${os}
|
|
|
2170
2192
|
function actionBadge(action) {
|
|
2171
2193
|
var labels = {
|
|
2172
2194
|
'proposal.created': 'Created',
|
|
2195
|
+
'proposal.late_deploy_overwrite': 'Late deploy warning',
|
|
2173
2196
|
'proposal.approved': 'Approved',
|
|
2174
2197
|
'proposal.rejected': 'Rejected',
|
|
2175
2198
|
'proposal.failed': 'Failed',
|
|
@@ -2191,6 +2214,7 @@ ${os}
|
|
|
2191
2214
|
function actionFilterLabel(action) {
|
|
2192
2215
|
var labels = {
|
|
2193
2216
|
'proposal.created': 'Created',
|
|
2217
|
+
'proposal.late_deploy_overwrite': 'Late deploy warning',
|
|
2194
2218
|
'proposal.approved': 'Approved',
|
|
2195
2219
|
'proposal.rejected': 'Rejected',
|
|
2196
2220
|
'proposal.failed': 'Failed',
|
|
@@ -2236,7 +2260,7 @@ ${os}
|
|
|
2236
2260
|
}
|
|
2237
2261
|
return '';
|
|
2238
2262
|
}
|
|
2239
|
-
`}a(
|
|
2263
|
+
`}a(Ug,"pageClientScriptPartAFormat");function Bg(){return`
|
|
2240
2264
|
function lookupCachedProposal(id) {
|
|
2241
2265
|
if (!id) return null;
|
|
2242
2266
|
return cachedProposals.filter(function (x) { return x.id === id; })[0] || null;
|
|
@@ -2357,7 +2381,7 @@ ${os}
|
|
|
2357
2381
|
return k + '=' + String(v);
|
|
2358
2382
|
}).join(', '));
|
|
2359
2383
|
}
|
|
2360
|
-
`}a(
|
|
2384
|
+
`}a(Bg,"pageClientScriptPartAAudit");function qg(){return`
|
|
2361
2385
|
// Hash helpers \u2014 keep in sync with page-hash.ts
|
|
2362
2386
|
var TAB_IDS = ['status', 'settings', 'links', 'oauth', 'proposals'];
|
|
2363
2387
|
function hashForTab(tab) {
|
|
@@ -2367,7 +2391,7 @@ ${os}
|
|
|
2367
2391
|
}
|
|
2368
2392
|
|
|
2369
2393
|
function tabFromHash(hash) {
|
|
2370
|
-
var raw = String(hash || '').replace(/^#/, '');
|
|
2394
|
+
var raw = String(hash || '').replace(/^#/, '').replace(/^section-/, '');
|
|
2371
2395
|
if (!raw || raw.indexOf('proposal=') === 0) return '';
|
|
2372
2396
|
if (raw === 'audit' || raw === 'logs') return 'logs';
|
|
2373
2397
|
if (raw === 'overview') return 'status';
|
|
@@ -2410,14 +2434,19 @@ ${os}
|
|
|
2410
2434
|
});
|
|
2411
2435
|
}
|
|
2412
2436
|
|
|
2437
|
+
var selectedLocationHash;
|
|
2413
2438
|
function selectTab(target, options) {
|
|
2414
2439
|
options = options || {};
|
|
2415
2440
|
if (!target) return;
|
|
2416
2441
|
document.querySelectorAll('nav.tabs button').forEach(function (b) {
|
|
2417
|
-
b.setAttribute('aria-
|
|
2442
|
+
b.setAttribute('aria-current', b.getAttribute('data-tab') === target ? 'page' : 'false');
|
|
2418
2443
|
});
|
|
2419
2444
|
document.querySelectorAll('section.panel').forEach(function (sec) {
|
|
2420
2445
|
sec.classList.toggle('active', sec.id === 'section-' + target);
|
|
2446
|
+
if (options.focusHeading && sec.id === 'section-' + target) {
|
|
2447
|
+
var heading = sec.querySelector('h2');
|
|
2448
|
+
if (heading) heading.focus();
|
|
2449
|
+
}
|
|
2421
2450
|
});
|
|
2422
2451
|
if (target !== 'proposals') {
|
|
2423
2452
|
clearProposalHighlight();
|
|
@@ -2429,6 +2458,7 @@ ${os}
|
|
|
2429
2458
|
replaceLocationHash('#' + hashForTab(target));
|
|
2430
2459
|
}
|
|
2431
2460
|
}
|
|
2461
|
+
selectedLocationHash = String(location.hash || '');
|
|
2432
2462
|
}
|
|
2433
2463
|
|
|
2434
2464
|
function loadTabData(target) {
|
|
@@ -2446,7 +2476,10 @@ ${os}
|
|
|
2446
2476
|
document.querySelectorAll('nav.tabs button').forEach(function (btn) {
|
|
2447
2477
|
btn.addEventListener('click', function () {
|
|
2448
2478
|
var target = btn.getAttribute('data-tab');
|
|
2449
|
-
|
|
2479
|
+
if (location.hash !== '#' + hashForTab(target)) {
|
|
2480
|
+
history.pushState(null, '', location.pathname + location.search + '#' + hashForTab(target));
|
|
2481
|
+
}
|
|
2482
|
+
selectTab(target, { skipHash: true });
|
|
2450
2483
|
loadTabData(target);
|
|
2451
2484
|
});
|
|
2452
2485
|
});
|
|
@@ -2643,7 +2676,7 @@ ${os}
|
|
|
2643
2676
|
}
|
|
2644
2677
|
}
|
|
2645
2678
|
|
|
2646
|
-
`}a(
|
|
2679
|
+
`}a(qg,"pageClientScriptPartATabs");function Wg(){return`
|
|
2647
2680
|
async function loadLinks(opts) {
|
|
2648
2681
|
opts = opts || {};
|
|
2649
2682
|
if (!aiOn || !canManage) {
|
|
@@ -2717,7 +2750,7 @@ ${os}
|
|
|
2717
2750
|
showToast(e.message || String(e), false);
|
|
2718
2751
|
}
|
|
2719
2752
|
}
|
|
2720
|
-
`}a(
|
|
2753
|
+
`}a(Wg,"pageClientScriptPartA2");function zg(){return`
|
|
2721
2754
|
|
|
2722
2755
|
function normalizeLinkKind(kind) {
|
|
2723
2756
|
if (kind === 'rest' || kind === 'mcp' || kind === 'get_link') return kind;
|
|
@@ -3175,6 +3208,7 @@ ${os}
|
|
|
3175
3208
|
|
|
3176
3209
|
function plainDiffSentence(diff, flowLabel) {
|
|
3177
3210
|
if (!diff || !diff.summary) return '';
|
|
3211
|
+
if (proposalHasNoNetChange(diff)) return 'Nothing changes in the live flow';
|
|
3178
3212
|
var sum = diff.summary;
|
|
3179
3213
|
var parts = [];
|
|
3180
3214
|
if (sum.added) parts.push('Adds ' + sum.added + (sum.added === 1 ? ' node' : ' nodes'));
|
|
@@ -3182,7 +3216,8 @@ ${os}
|
|
|
3182
3216
|
(sum.removed === 1 ? ' node' : ' nodes'));
|
|
3183
3217
|
if (sum.modified) parts.push((parts.length ? 'changes ' : 'Changes ') + sum.modified +
|
|
3184
3218
|
(sum.modified === 1 ? ' node' : ' nodes'));
|
|
3185
|
-
if (
|
|
3219
|
+
if ((sum.tabChanges || []).length) parts.push('Changes flow settings');
|
|
3220
|
+
if (!parts.length) parts.push('No changes');
|
|
3186
3221
|
var sentence = parts.join(', ');
|
|
3187
3222
|
if (flowLabel) sentence += ' in ' + flowLabel;
|
|
3188
3223
|
return sentence;
|
|
@@ -3216,10 +3251,14 @@ ${os}
|
|
|
3216
3251
|
|
|
3217
3252
|
/** Soft validation notes from the proposal and/or its diff. */
|
|
3218
3253
|
function proposalWarnings(proposal, diff) {
|
|
3219
|
-
var
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
return
|
|
3254
|
+
var all = (proposal && Array.isArray(proposal.warnings) ? proposal.warnings : [])
|
|
3255
|
+
.concat(diff && Array.isArray(diff.warnings) ? diff.warnings : []);
|
|
3256
|
+
var seen = {};
|
|
3257
|
+
return all.filter(function (w) {
|
|
3258
|
+
var key = JSON.stringify([w.code, w.nodeId, w.property, w.error]);
|
|
3259
|
+
if (seen[key]) return false;
|
|
3260
|
+
seen[key] = true; return true;
|
|
3261
|
+
});
|
|
3223
3262
|
}
|
|
3224
3263
|
|
|
3225
3264
|
/**
|
|
@@ -3228,33 +3267,22 @@ ${os}
|
|
|
3228
3267
|
*/
|
|
3229
3268
|
var SECRET_WARN_CODES = ['secret_destination_changed', 'secret_node_code_changed',
|
|
3230
3269
|
'secret_wires_changed', 'secret_scope_changed', 'outbound_node_changed', 'secret_tab_changed',
|
|
3231
|
-
'secret_reference_added'];
|
|
3232
|
-
function
|
|
3270
|
+
'secret_reference_added', 'secret_may_revert'];
|
|
3271
|
+
function renderReviewWarnings(p, diff) {
|
|
3272
|
+
if (proposalHasNoNetChange(diff)) return '';
|
|
3273
|
+
var warnings = proposalWarnings(p, diff);
|
|
3274
|
+
var security = warnings.filter(function (w) { return SECRET_WARN_CODES.indexOf(w.code) >= 0; });
|
|
3275
|
+
var config = warnings.filter(function (w) { return SECRET_WARN_CODES.indexOf(w.code) < 0; });
|
|
3233
3276
|
var fields = p && Array.isArray(p.restoredSecrets) ? p.restoredSecrets : [];
|
|
3234
|
-
var
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
return '<li><strong>' + esc(String(w.code)) + '</strong> \u2014 ' + esc(String(w.error || '')) + '</li>';
|
|
3244
|
-
}).join('');
|
|
3245
|
-
return '<div class="proposal-warnings restored-secrets' + (strong ? ' restored-secrets-danger' : '') +
|
|
3246
|
-
'" role="' + (strong ? 'alert' : 'status') + '">' +
|
|
3247
|
-
'<p class="proposal-warnings-title">' +
|
|
3248
|
-
esc(strong
|
|
3249
|
-
? 'Security review: this proposal changes what could send data (or secrets) elsewhere'
|
|
3250
|
-
: 'Secrets restored from the live flow (' + fields.length + ')') +
|
|
3251
|
-
'</p>' +
|
|
3252
|
-
(warnItems ? '<ul>' + warnItems + '</ul>' : '') +
|
|
3253
|
-
(fields.length
|
|
3254
|
-
? '<p class="muted">The AI saw [REDACTED] in these fields; approving keeps the real values:</p>' +
|
|
3255
|
-
'<ul>' + items + '</ul>'
|
|
3256
|
-
: '') +
|
|
3257
|
-
'</div>';
|
|
3277
|
+
var count = warnings.length + fields.length;
|
|
3278
|
+
if (!count) return '';
|
|
3279
|
+
return '<aside class="proposal-warnings review-warnings" role="' + (security.length ? 'alert' : 'status') + '">' +
|
|
3280
|
+
'<h3>Review notes (' + count + ')</h3>' +
|
|
3281
|
+
renderValidationWarnings(security, { title: 'Security review (' + security.length + ')' }) +
|
|
3282
|
+
(fields.length ? '<details class="restored-secrets"><summary>Secrets retained from the live flow (' + fields.length + ')</summary><ul>' + fields.map(function (f) {
|
|
3283
|
+
return '<li><code>' + esc(f.nodeId) + '</code> \xB7 ' + esc(f.property) + '</li>';
|
|
3284
|
+
}).join('') + '</ul></details>' : '') +
|
|
3285
|
+
renderValidationWarnings(config, { title: 'Needs configuration (' + config.length + ')' }) + '</aside>';
|
|
3258
3286
|
}
|
|
3259
3287
|
|
|
3260
3288
|
function renderValidationWarnings(warnings, opts) {
|
|
@@ -3447,7 +3475,7 @@ ${os}
|
|
|
3447
3475
|
decided: Math.max(decidedTotal, cachedProposals.filter(function (p) { return (p.status !== 'pending' && p.status !== 'conflict'); }).length)
|
|
3448
3476
|
};
|
|
3449
3477
|
}
|
|
3450
|
-
`}a(
|
|
3478
|
+
`}a(zg,"pageClientScriptPartB");function Gg(){return`
|
|
3451
3479
|
var DIFF_MODE_KEY = 'ai-access-diff-mode';
|
|
3452
3480
|
var DIFF_SPLIT_MIN_PX = ${900};
|
|
3453
3481
|
var PRESENCE_SHORT_TEXT_MAX = ${120};
|
|
@@ -3564,7 +3592,7 @@ ${os}
|
|
|
3564
3592
|
'</div>';
|
|
3565
3593
|
}
|
|
3566
3594
|
|
|
3567
|
-
`}a(
|
|
3595
|
+
`}a(Gg,"pageClientScriptPartBDiff");function Kg(){return`
|
|
3568
3596
|
function isCollapsiblePresenceText(value) {
|
|
3569
3597
|
var s = String(value == null ? '' : value);
|
|
3570
3598
|
if (s.indexOf('\\n') >= 0) return true;
|
|
@@ -3667,7 +3695,7 @@ ${os}
|
|
|
3667
3695
|
});
|
|
3668
3696
|
});
|
|
3669
3697
|
}
|
|
3670
|
-
`}a(
|
|
3698
|
+
`}a(Kg,"pageClientScriptPartBDiffPresence");function Jg(){return`
|
|
3671
3699
|
function textChangeLineCount(ch) {
|
|
3672
3700
|
return (ch.lines || []).length;
|
|
3673
3701
|
}
|
|
@@ -3726,10 +3754,13 @@ ${os}
|
|
|
3726
3754
|
'<code class="diff-before" title="before">' + esc(JSON.stringify(ch.before)) + '</code>' +
|
|
3727
3755
|
' \u2192 <code class="diff-after" title="after">' + esc(JSON.stringify(ch.after)) + '</code></div>';
|
|
3728
3756
|
}
|
|
3729
|
-
`}a(
|
|
3757
|
+
`}a(Jg,"pageClientScriptPartBDiffView");function Vg(){return`
|
|
3730
3758
|
function renderDiffSections(sections, mode, emptyMsg) {
|
|
3731
3759
|
var parts = sections.summary && (sections.summary.tabChanges || []).length
|
|
3732
3760
|
? '<p>Flow settings changed: ' + esc(sections.summary.tabChanges.join(', ')) + '</p>' : '';
|
|
3761
|
+
if (sections.tabProperties && sections.tabProperties.length) {
|
|
3762
|
+
parts += sections.tabProperties.map(function (ch) { return renderPropertyChange(ch, mode); }).join('');
|
|
3763
|
+
}
|
|
3733
3764
|
if (sections.added && sections.added.length) {
|
|
3734
3765
|
parts += '<div class="diff-section"><h4>Added nodes</h4>' +
|
|
3735
3766
|
sections.added.map(function (n) { return renderPresenceNode(n, 'added'); }).join('') +
|
|
@@ -3763,6 +3794,23 @@ ${os}
|
|
|
3763
3794
|
return parts;
|
|
3764
3795
|
}
|
|
3765
3796
|
|
|
3797
|
+
function diffHasChanges(diff) {
|
|
3798
|
+
if (!diff || !diff.summary) return false;
|
|
3799
|
+
var sum = diff.summary;
|
|
3800
|
+
return !!(sum.added || sum.removed || sum.modified || (sum.tabChanges || []).length);
|
|
3801
|
+
}
|
|
3802
|
+
function proposalHasNoNetChange(diff) {
|
|
3803
|
+
return !!(diff && diff.rebase && diff.rebase.state !== 'conflict' &&
|
|
3804
|
+
diff.vsCurrent && diff.vsCurrent.summary && !diffHasChanges(diff.vsCurrent));
|
|
3805
|
+
}
|
|
3806
|
+
function diffComparisonKey(diff) {
|
|
3807
|
+
if (!diff) return '';
|
|
3808
|
+
return JSON.stringify([diff.summary && diff.summary.tabChanges, diff.tabProperties || [],
|
|
3809
|
+
(diff.added || []).map(function (n) { return [n.id, n.properties]; }),
|
|
3810
|
+
(diff.removed || []).map(function (n) { return [n.id, n.properties]; }),
|
|
3811
|
+
(diff.modified || []).map(function (n) { return [n.id, n.changes]; })]);
|
|
3812
|
+
}
|
|
3813
|
+
|
|
3766
3814
|
function renderHumanDiff(diff, mode) {
|
|
3767
3815
|
if (!diff) return '<p class="muted">Diff unavailable.</p>';
|
|
3768
3816
|
mode = mode || getDiffMode();
|
|
@@ -3784,61 +3832,121 @@ ${os}
|
|
|
3784
3832
|
(mode === 'unified' ? 'true' : 'false') + '">Unified</button>' +
|
|
3785
3833
|
'</div></div>';
|
|
3786
3834
|
}
|
|
3787
|
-
var
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
mode,
|
|
3794
|
-
diff.compareAgainst === 'base'
|
|
3795
|
-
? 'No node changes compared with what the AI started from.'
|
|
3796
|
-
: 'No node changes compared with the live flow.'
|
|
3797
|
-
);
|
|
3798
|
-
var liveChanges = diff.liveChanges;
|
|
3799
|
-
var liveHtml = liveChanges && ((liveChanges.added || []).length ||
|
|
3800
|
-
(liveChanges.removed || []).length || (liveChanges.modified || []).length ||
|
|
3801
|
-
(liveChanges.summary && (liveChanges.summary.tabChanges || []).length))
|
|
3802
|
-
? '<h3>Changed in editor since submit</h3>' + renderDiffSections(liveChanges, mode, 'No editor node changes.') : '';
|
|
3835
|
+
var sections = renderDiffSections(diff, mode, 'Nothing changes.');
|
|
3836
|
+
var liveChanged = diffHasChanges(diff.liveChanges);
|
|
3837
|
+
var identical = diff.vsCurrent && diffComparisonKey(diff) === diffComparisonKey(diff.vsCurrent);
|
|
3838
|
+
var liveHtml = liveChanged
|
|
3839
|
+
? '<details class="editor-changes"><summary>Changed in editor since submit</summary>' +
|
|
3840
|
+
renderDiffSections(diff.liveChanges, mode, 'No editor changes.') + '</details>' : '';
|
|
3803
3841
|
var deployHtml = diff.vsCurrent
|
|
3804
|
-
? renderDiffSections(diff.vsCurrent, mode, '
|
|
3805
|
-
: '<p class="muted">
|
|
3806
|
-
|
|
3807
|
-
|
|
3842
|
+
? renderDiffSections(diff.vsCurrent, mode, 'Nothing changes in the live flow.')
|
|
3843
|
+
: '<p class="muted">Resolve conflicts to preview what will deploy.</p>';
|
|
3844
|
+
if (proposalHasNoNetChange(diff)) {
|
|
3845
|
+
return '<p class="no-net-change" role="status"><strong>Nothing changes in the live flow.</strong> ' +
|
|
3846
|
+
'The proposal is already reflected in the editor. You can reject it to close it.</p>' + liveHtml;
|
|
3847
|
+
}
|
|
3848
|
+
if (identical || (!liveChanged && !(diff.rebase && diff.rebase.state === 'conflict'))) {
|
|
3849
|
+
return missing + toolbar + '<h3>Proposed change</h3>' + sections + liveHtml;
|
|
3850
|
+
}
|
|
3851
|
+
return missing + toolbar + '<h3>Proposed change (vs base)</h3>' + sections +
|
|
3808
3852
|
liveHtml + '<h3>Will deploy (vs live)</h3>' + deployHtml;
|
|
3809
3853
|
}
|
|
3810
3854
|
|
|
3855
|
+
function conflictReason(c) {
|
|
3856
|
+
var reasons = {
|
|
3857
|
+
node_changed_both: 'The editor and proposal changed the same fields on this node.',
|
|
3858
|
+
tab_changed_both: 'The editor and proposal changed the same flow settings.',
|
|
3859
|
+
node_deleted_live: 'This node was deleted in the editor and edited in the proposal.',
|
|
3860
|
+
node_deleted_in_proposal_modified_live: 'The proposal deletes a node that was edited in the editor.',
|
|
3861
|
+
id_collision: 'The editor and proposal added different nodes with the same ID.',
|
|
3862
|
+
tab_deleted: 'This flow was deleted in the editor.',
|
|
3863
|
+
base_unavailable: 'The original flow snapshot is unavailable. Ask the AI to submit a new proposal.',
|
|
3864
|
+
dangling_reference: 'A connection or configuration refers to a missing node.',
|
|
3865
|
+
redacted_placeholder: 'A hidden secret could not be matched safely. Review it in the editor.'
|
|
3866
|
+
};
|
|
3867
|
+
return reasons[c.kind] || c.message || 'Review the competing changes.';
|
|
3868
|
+
}
|
|
3869
|
+
function conflictValueText(value) {
|
|
3870
|
+
return value === undefined ? '(not present)' : typeof value === 'string' ? value : JSON.stringify(value, null, 2);
|
|
3871
|
+
}
|
|
3872
|
+
function renderConflictValues(c, diff) {
|
|
3873
|
+
var entry = ((diff && diff.conflictValues) || []).filter(function (v) { return v.nodeId === (c.nodeId || ''); })[0];
|
|
3874
|
+
if (!entry) return '<p class="muted">Loading before and after values\u2026</p>';
|
|
3875
|
+
return entry.fields.map(function (v) {
|
|
3876
|
+
return '<div class="conflict-values"><h4>' + esc(v.field || 'Node') + '</h4><dl>' +
|
|
3877
|
+
['base', 'live', 'proposed'].map(function (side) {
|
|
3878
|
+
return '<div><dt>' + (side === 'base' ? 'Base (before submission)' : side === 'live' ? 'Live (in editor)' : 'Proposed (by AI)') +
|
|
3879
|
+
'</dt><dd><pre>' + esc(conflictValueText(v[side])) + '</pre></dd></div>';
|
|
3880
|
+
}).join('') + '</dl></div>';
|
|
3881
|
+
}).join('');
|
|
3882
|
+
}
|
|
3883
|
+
|
|
3811
3884
|
function renderConflictPanel(p, diff) {
|
|
3812
3885
|
var rebase = (diff && diff.rebase) || p.rebase;
|
|
3813
|
-
|
|
3814
|
-
|
|
3886
|
+
var selected = p.conflictChoice;
|
|
3887
|
+
var chosen = selected ? '<div class="conflict-choice" role="status"><strong>Chosen: ' +
|
|
3888
|
+
(selected.choice === 'live' ? 'Keep live' : 'Use proposed') + '</strong> \xB7 ' + esc(selected.nodeId) +
|
|
3889
|
+
(canApprove ? ' <button type="button" class="action secondary resolve-conflict-btn" data-id="' + esc(p.id) +
|
|
3890
|
+
'" data-node-id="' + esc(selected.nodeId) + '" data-choice="undo">Undo / change choice</button>' : '') +
|
|
3891
|
+
'<p>This choice changes the proposal only. Nothing is deployed until approval. Undo is available until the next proposal edit or choice.</p></div>' : '';
|
|
3892
|
+
if ((p.aiConflictOverrides || []).length) {
|
|
3893
|
+
chosen += '<div class="warning ai-conflict-override" role="alert"><strong>AI chose its own value over your editor change</strong>' +
|
|
3894
|
+
'<p>Review the fields below before approving this proposal.</p>' +
|
|
3895
|
+
((diff && diff.aiOverrideValues) || []).map(function (entry) {
|
|
3896
|
+
return '<h4>' + esc(entry.nodeId) + '</h4>' + entry.fields.map(function (v) {
|
|
3897
|
+
return '<div class="conflict-values"><h4>' + esc(v.field || 'Node') + '</h4><dl>' +
|
|
3898
|
+
'<div><dt>Before (live editor)</dt><dd><pre>' + esc(conflictValueText(v.live)) + '</pre></dd></div>' +
|
|
3899
|
+
'<div><dt>After (AI proposed)</dt><dd><pre>' + esc(conflictValueText(v.proposed)) + '</pre></dd></div></dl></div>';
|
|
3900
|
+
}).join('');
|
|
3901
|
+
}).join('') + (!diff ? '<p>Loading before and after values\u2026</p>' : '') + '</div>';
|
|
3902
|
+
}
|
|
3903
|
+
if (!rebase || rebase.state !== 'conflict') return chosen;
|
|
3904
|
+
return chosen + '<div class="proposal-conflicts" role="region" aria-label="Proposal conflicts">' +
|
|
3815
3905
|
'<h3>Conflicts</h3><p>Resolve these conflicts before approving.</p>' +
|
|
3816
3906
|
(rebase.conflicts || []).map(function (c) {
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
'
|
|
3907
|
+
var tabConflict = c.nodeId === proposalFlowId(p) || /^tab_/.test(c.kind || '');
|
|
3908
|
+
return '<div class="proposal-conflict"><strong>' + esc(flowLabelOf(p) || 'Flow') + ' \xB7 ' +
|
|
3909
|
+
esc(c.nodeName || (tabConflict ? 'Flow settings' : 'Unnamed node')) + '</strong>' +
|
|
3910
|
+
' \xB7 ' + esc(c.nodeType || (tabConflict ? 'tab' : 'unknown type')) + ' \xB7 <code>' + esc(c.nodeId || '') + '</code>' +
|
|
3911
|
+
'<p>' + esc(conflictReason(c)) + '</p>' + renderConflictValues(c, diff) +
|
|
3912
|
+
(canApprove ? (c.choices || []).filter(function (choice) {
|
|
3821
3913
|
return choice === 'live' || choice === 'proposed';
|
|
3822
3914
|
}).map(function (choice) {
|
|
3823
|
-
return '<button type="button" class="action secondary resolve-conflict-btn" data-id="' + esc(p.id) +
|
|
3915
|
+
return '<div class="conflict-option"><button type="button" class="action secondary resolve-conflict-btn" data-id="' + esc(p.id) +
|
|
3824
3916
|
'" data-node-id="' + esc(c.nodeId || '') + '" data-choice="' + esc(choice) + '">' +
|
|
3825
|
-
(choice === 'live' ? 'Keep live' : 'Use proposed') + '</button>
|
|
3917
|
+
(choice === 'live' ? 'Keep live' : 'Use proposed') + '</button>' +
|
|
3918
|
+
'<p class="hint">' + (choice === 'live' ? 'Keep the editor value for the conflicting fields.' :
|
|
3919
|
+
'Replace the conflicting editor fields with the AI values.') + ' Other field changes are preserved.</p></div>';
|
|
3826
3920
|
}).join('') : '') + '</div>';
|
|
3827
3921
|
}).join('') + (canApprove ? '<button type="button" class="action secondary recheck-btn" data-id="' +
|
|
3828
3922
|
esc(p.id) + '">Re-check</button>' : '') + '</div>';
|
|
3829
3923
|
}
|
|
3830
3924
|
|
|
3831
|
-
async function updateProposalConflict(id, nodeId, choice) {
|
|
3925
|
+
async function updateProposalConflict(id, nodeId, choice, button) {
|
|
3926
|
+
proposalActionsInFlight += 1;
|
|
3927
|
+
var label = button && button.textContent;
|
|
3928
|
+
if (button) {
|
|
3929
|
+
button.disabled = true;
|
|
3930
|
+
button.setAttribute('aria-busy', 'true');
|
|
3931
|
+
button.innerHTML = '<span class="spinner" aria-hidden="true"></span> ' + (choice ? 'Saving\u2026' : 'Re-checking\u2026');
|
|
3932
|
+
}
|
|
3832
3933
|
try {
|
|
3833
|
-
await api('/proposals/' + encodeURIComponent(id) + (choice ? '/resolve-conflict' : '/recheck'), {
|
|
3934
|
+
var result = await api('/proposals/' + encodeURIComponent(id) + (choice ? '/resolve-conflict' : '/recheck'), {
|
|
3834
3935
|
method: 'POST', body: JSON.stringify(choice ? { nodeId: nodeId, choice: choice } : {})
|
|
3835
3936
|
});
|
|
3836
3937
|
delete proposalDiffCache[id];
|
|
3837
3938
|
await loadProposals();
|
|
3838
3939
|
await loadProposalDiff(id);
|
|
3940
|
+
var current = cachedProposals.filter(function (p) { return p.id === id; })[0];
|
|
3941
|
+
var conflict = current ? proposalIsStale(current) : result && result.proposal && result.proposal.status === 'conflict';
|
|
3942
|
+
showToast(choice === 'undo' ? 'Choice undone. Review the conflict again.' :
|
|
3943
|
+
conflict ? 'Still conflicting. Review the remaining conflicts.' : 'Resolved. Review the updated proposal.', true);
|
|
3839
3944
|
notifySidebarPendingRefresh();
|
|
3840
3945
|
} catch (e) {
|
|
3841
3946
|
showFlash($('proposals-flash'), e.message || String(e), false);
|
|
3947
|
+
} finally {
|
|
3948
|
+
proposalActionsInFlight -= 1;
|
|
3949
|
+
if (button) { button.disabled = false; button.removeAttribute('aria-busy'); button.textContent = label; }
|
|
3842
3950
|
}
|
|
3843
3951
|
}
|
|
3844
3952
|
|
|
@@ -3852,7 +3960,7 @@ ${os}
|
|
|
3852
3960
|
// Full view kept for mode-toggle re-render inside .proposal-changes.
|
|
3853
3961
|
return renderHumanDiff(diff, mode);
|
|
3854
3962
|
}
|
|
3855
|
-
`}a(
|
|
3963
|
+
`}a(Vg,"pageClientScriptPartBDiffView2");function Yg(){return`
|
|
3856
3964
|
function updatePendingBadges() {
|
|
3857
3965
|
var counts = countPendingBuckets();
|
|
3858
3966
|
var fullText = formatPendingBadgeText(counts.live, counts.stale);
|
|
@@ -3864,18 +3972,28 @@ ${os}
|
|
|
3864
3972
|
badge.title = fullText;
|
|
3865
3973
|
}
|
|
3866
3974
|
var filterCount = $('filter-pending-count');
|
|
3867
|
-
if (filterCount) filterCount.textContent = fullText;
|
|
3975
|
+
if (filterCount) { filterCount.textContent = String(counts.pending); filterCount.title = fullText; }
|
|
3868
3976
|
var decidedCount = $('filter-decided-count');
|
|
3869
3977
|
if (decidedCount) decidedCount.textContent = String(counts.decided);
|
|
3870
3978
|
var dashPending = $('dash-pending');
|
|
3871
3979
|
if (dashPending) {
|
|
3872
|
-
dashPending.textContent =
|
|
3980
|
+
dashPending.textContent = String(counts.pending);
|
|
3873
3981
|
dashPending.title = fullText;
|
|
3874
3982
|
}
|
|
3875
3983
|
}
|
|
3876
3984
|
|
|
3877
3985
|
function setProposalsFilter(next) {
|
|
3878
3986
|
proposalsFilter = next === 'decided' ? 'decided' : 'pending';
|
|
3987
|
+
var blockBanner = document.getElementById('deploy-unknown-blocks');
|
|
3988
|
+
if (!blockBanner && list.parentNode) {
|
|
3989
|
+
blockBanner = document.createElement('div');
|
|
3990
|
+
blockBanner.id = 'deploy-unknown-blocks';
|
|
3991
|
+
list.parentNode.insertBefore(blockBanner, list);
|
|
3992
|
+
}
|
|
3993
|
+
if (blockBanner) {
|
|
3994
|
+
blockBanner.innerHTML = renderDeployUnknownBlocks(deployUnknownBlocks);
|
|
3995
|
+
bindProposalActions(blockBanner);
|
|
3996
|
+
}
|
|
3879
3997
|
var filters = $('proposals-filters');
|
|
3880
3998
|
if (filters) {
|
|
3881
3999
|
filters.querySelectorAll('.filter-chip').forEach(function (chip) {
|
|
@@ -3908,11 +4026,18 @@ ${os}
|
|
|
3908
4026
|
});
|
|
3909
4027
|
});
|
|
3910
4028
|
list.querySelectorAll('.resolve-conflict-btn, .recheck-btn').forEach(function (btn) {
|
|
3911
|
-
bindOnce(btn, 'click',
|
|
4029
|
+
bindOnce(btn, 'click', function () {
|
|
3912
4030
|
if (btn.disabled) return;
|
|
3913
|
-
btn.
|
|
3914
|
-
|
|
3915
|
-
|
|
4031
|
+
var id = btn.getAttribute('data-id'), nodeId = btn.getAttribute('data-node-id'), choice = btn.getAttribute('data-choice');
|
|
4032
|
+
if (!choice) { updateProposalConflict(id, nodeId, choice, btn); return; }
|
|
4033
|
+
openConfirmDialog({
|
|
4034
|
+
title: choice === 'undo' ? 'Undo this choice?' : choice === 'live' ? 'Keep live values?' : 'Use proposed values?',
|
|
4035
|
+
message: choice === 'undo' ? 'Restore the proposal before the latest choice, then re-check against the current editor.' :
|
|
4036
|
+
(choice === 'live' ? 'The conflicting fields will keep the editor values.' : 'The conflicting fields will use the AI values.') +
|
|
4037
|
+
' Independent field changes are preserved. This updates the proposal; it does not deploy.',
|
|
4038
|
+
confirmLabel: choice === 'undo' ? 'Undo choice' : 'Confirm choice',
|
|
4039
|
+
onConfirm: function () { updateProposalConflict(id, nodeId, choice, btn); }
|
|
4040
|
+
});
|
|
3916
4041
|
});
|
|
3917
4042
|
});
|
|
3918
4043
|
list.querySelectorAll('.approve-btn').forEach(function (btn) {
|
|
@@ -3921,6 +4046,23 @@ ${os}
|
|
|
3921
4046
|
approveProposal(btn.getAttribute('data-id'));
|
|
3922
4047
|
});
|
|
3923
4048
|
});
|
|
4049
|
+
list.querySelectorAll('.confirm-live-checked-btn').forEach(function (btn) {
|
|
4050
|
+
bindOnce(btn, 'click', async function () {
|
|
4051
|
+
openConfirmDialog({
|
|
4052
|
+
title: 'Confirm live flows checked',
|
|
4053
|
+
message: 'Have you checked the live flows? This clears the global approval block, but the earlier deploy may still land.',
|
|
4054
|
+
confirmLabel: 'Confirm live checked',
|
|
4055
|
+
danger: true,
|
|
4056
|
+
onConfirm: async function () {
|
|
4057
|
+
try {
|
|
4058
|
+
await api('/proposals/' + encodeURIComponent(btn.getAttribute('data-id')) + '/confirm-live-checked', { method: 'POST', body: JSON.stringify({ attemptId: btn.getAttribute('data-attempt-id') }) });
|
|
4059
|
+
await loadProposals();
|
|
4060
|
+
loadLogs();
|
|
4061
|
+
} catch (e) { showToast(e.message || String(e), false); }
|
|
4062
|
+
}
|
|
4063
|
+
});
|
|
4064
|
+
});
|
|
4065
|
+
});
|
|
3924
4066
|
list.querySelectorAll('.reject-btn').forEach(function (btn) {
|
|
3925
4067
|
bindOnce(btn, 'click', function () {
|
|
3926
4068
|
openRejectDialog(btn.getAttribute('data-id'));
|
|
@@ -3958,7 +4100,7 @@ ${os}
|
|
|
3958
4100
|
});
|
|
3959
4101
|
});
|
|
3960
4102
|
}
|
|
3961
|
-
`}a(
|
|
4103
|
+
`}a(Yg,"pageClientScriptPartB2");function Zg(){return`
|
|
3962
4104
|
function renderDeleteBanner(diff) {
|
|
3963
4105
|
if (!isLargeDelete(diff)) return '';
|
|
3964
4106
|
var beforeN = diff.summary.beforeNodeCount || 0;
|
|
@@ -3970,16 +4112,10 @@ ${os}
|
|
|
3970
4112
|
function renderPendingActions(p, stale) {
|
|
3971
4113
|
if (!canApprove || (p.status !== 'pending' && p.status !== 'conflict')) return '';
|
|
3972
4114
|
var cached = proposalDiffCache[p.id];
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
' configuration warning' + (warns.length === 1 ? '' : 's') +
|
|
3978
|
-
' \u2014 you can finish setup in the editor afterward'
|
|
3979
|
-
})
|
|
3980
|
-
: '';
|
|
3981
|
-
return warnNote +
|
|
3982
|
-
'<p class="muted approve-help">Approve deploys this change to Node-RED. Nothing is live until you approve.</p>' +
|
|
4115
|
+
if (proposalHasNoNetChange(cached)) {
|
|
4116
|
+
return '<button type="button" class="action secondary reject-btn" data-id="' + esc(p.id) + '">Reject to close</button>';
|
|
4117
|
+
}
|
|
4118
|
+
return '<p class="muted approve-help">Approve deploys this change to Node-RED. Nothing is live until you approve.</p>' +
|
|
3983
4119
|
'<div class="row">' +
|
|
3984
4120
|
'<button type="button" class="action approve-btn" data-id="' + esc(p.id) +
|
|
3985
4121
|
'"' + (stale ? ' disabled title="Resolve conflicts before approving"' : (!cached ? ' disabled title="Wait for the diff to load"' : '')) + '>Approve & deploy</button>' +
|
|
@@ -4002,7 +4138,7 @@ ${os}
|
|
|
4002
4138
|
'</details>';
|
|
4003
4139
|
}
|
|
4004
4140
|
|
|
4005
|
-
`}a(
|
|
4141
|
+
`}a(Zg,"pageClientScriptPartB2Actions");function Xg(){return`
|
|
4006
4142
|
function renderRequestedVia(p) {
|
|
4007
4143
|
if (!p.createdBy) return '';
|
|
4008
4144
|
if (p.createdBy.label) {
|
|
@@ -4055,7 +4191,7 @@ ${os}
|
|
|
4055
4191
|
|
|
4056
4192
|
function renderRebaseChip(p, diff) {
|
|
4057
4193
|
var rebase = (diff && diff.rebase) || p.rebase;
|
|
4058
|
-
var count =
|
|
4194
|
+
var count = rebase && rebase.liveChangedCount;
|
|
4059
4195
|
return '<span class="rebase-chip"' + (p.status === 'pending' && rebase && rebase.state === 'merged' ? '' : ' hidden') +
|
|
4060
4196
|
'>Rebased \xB7 ' + esc(count == null ? 0 : count) + ' live changes</span>';
|
|
4061
4197
|
}
|
|
@@ -4070,18 +4206,17 @@ ${os}
|
|
|
4070
4206
|
: '';
|
|
4071
4207
|
var plain = cached ? plainDiffSentence(cached, flowLabel) : '';
|
|
4072
4208
|
// Show for any large delete \u2014 including stale \u2014 so owners see the risk.
|
|
4073
|
-
var deleteBanner = cached ? renderDeleteBanner(cached) : '';
|
|
4209
|
+
var deleteBanner = cached && !proposalHasNoNetChange(cached) ? renderDeleteBanner(cached) : '';
|
|
4074
4210
|
// Card-level banner even before diff loads (warnings come from list API).
|
|
4075
|
-
|
|
4211
|
+
reason += p.deploymentAlert ? '<div role="alert" class="warning">' + esc(p.deploymentAlert) + '</div>' : '';
|
|
4212
|
+
reason += p.rejectedWhileDeployUnknown ? '<div role="alert" class="warning">Rejected while deploy outcome unknown. An earlier deploy may still land.</div>' : '';
|
|
4076
4213
|
var deployBanner = p.deployPending
|
|
4077
4214
|
? '<div class="proposal-warnings" role="status"><p class="proposal-warnings-title">Deploy outcome unknown</p><p>' +
|
|
4078
4215
|
esc(p.deployPending.queueReleased
|
|
4079
4216
|
? 'The approval queue was released. Check the live flow before approving again.'
|
|
4080
|
-
: p.deployPending.reason) + '</p></div>'
|
|
4217
|
+
: p.deployPending.reason) + '</p><p>Proposal ' + esc(p.id) + ' / attempt ' + esc(p.deployPending.attemptId || '') + ' / tabs: ' + esc(p.flowId || p.payload.flowId) + '</p><button type="button" class="confirm-live-checked-btn" data-attempt-id="' + esc(p.deployPending.attemptId || '') + '" data-id="' + esc(p.id) + '">Confirm live checked</button></div>'
|
|
4081
4218
|
: '';
|
|
4082
|
-
var warnBanner = deployBanner +
|
|
4083
|
-
? renderValidationWarnings(cardWarns)
|
|
4084
|
-
: '');
|
|
4219
|
+
var warnBanner = deployBanner + renderReviewWarnings(p, cached);
|
|
4085
4220
|
var stale = proposalIsStale(p);
|
|
4086
4221
|
var actions = renderPendingActions(p, stale);
|
|
4087
4222
|
var changes =
|
|
@@ -4094,28 +4229,32 @@ ${os}
|
|
|
4094
4229
|
'<header class="proposal-card-head">' +
|
|
4095
4230
|
'<h3 class="proposal-heading" data-title-for="' + esc(p.id) + '">' + esc(title) + '</h3>' +
|
|
4096
4231
|
'<span class="status-pill ' + esc(p.status) + '">' + esc(p.status === 'conflict' ? 'Conflict' : p.status) + '</span> ' +
|
|
4097
|
-
renderRebaseChip(p, cached) + renderProposalMetaLine(p) +
|
|
4232
|
+
renderLandedAfterWithdraw(p) + renderRebaseChip(p, cached) + renderProposalMetaLine(p) +
|
|
4098
4233
|
'</header>' +
|
|
4099
4234
|
(createdBy ? createdBy + '<br/>' : '') +
|
|
4100
4235
|
'<p class="proposal-plain-summary" data-plain-for="' + esc(p.id) + '">' +
|
|
4101
4236
|
(plain ? esc(plain) : '<span class="muted">Loading summary\u2026</span>') + '</p>' +
|
|
4102
4237
|
deleteBanner +
|
|
4103
|
-
warnBanner +
|
|
4238
|
+
'<div class="proposal-review-notes">' + warnBanner + '</div>' +
|
|
4104
4239
|
reason +
|
|
4105
4240
|
'<div class="proposal-conflict-host">' + renderConflictPanel(p, cached) + '</div>' +
|
|
4106
4241
|
changes +
|
|
4107
4242
|
renderProposalTech(p, cached) +
|
|
4108
|
-
actions +
|
|
4243
|
+
'<div class="proposal-actions">' + actions + '</div>' +
|
|
4109
4244
|
'</article>';
|
|
4110
4245
|
}
|
|
4111
|
-
`}a(
|
|
4246
|
+
`}a(Xg,"pageClientScriptPartB2Cards");function Qg(){return`
|
|
4247
|
+
function renderLandedAfterWithdraw(p) {
|
|
4248
|
+
return p.status === 'withdrawn' && p.landedAfterWithdraw
|
|
4249
|
+
? '<span class="status-pill withdrawn">Landed after withdraw</span> ' : '';
|
|
4250
|
+
}
|
|
4112
4251
|
function renderCompactProposal(p) {
|
|
4113
4252
|
var title = titleForProposalCard(p);
|
|
4114
4253
|
return '<div class="proposal-card-compact" data-id="' + esc(p.id) +
|
|
4115
4254
|
'" role="button" tabindex="0" aria-expanded="false">' +
|
|
4116
4255
|
'<strong>' + esc(title) + '</strong>' +
|
|
4117
4256
|
'<span class="status-pill ' + esc(p.status) + '">' + esc(p.status) + '</span>' +
|
|
4118
|
-
renderProposalMetaLine(p) +
|
|
4257
|
+
renderLandedAfterWithdraw(p) + renderProposalMetaLine(p) +
|
|
4119
4258
|
'</div>' +
|
|
4120
4259
|
'<div class="proposal-detail" id="proposal-' + esc(p.id) + '" data-id="' + esc(p.id) +
|
|
4121
4260
|
'" data-status="' + esc(p.status) + '" hidden></div>';
|
|
@@ -4176,9 +4315,27 @@ ${os}
|
|
|
4176
4315
|
'</div>';
|
|
4177
4316
|
}
|
|
4178
4317
|
|
|
4318
|
+
function renderDeployUnknownBlocks(blocks) {
|
|
4319
|
+
return blocks.map(function (b) {
|
|
4320
|
+
return '<div role="alert" class="proposal-warnings">All approvals blocked. Deployment target tabs: ' +
|
|
4321
|
+
esc(b.flowIds.join(', ')) + ' \u2014 proposal ' + esc(b.proposalId) + ', attempt ' + esc(b.attemptId || '') +
|
|
4322
|
+
'. An earlier deploy may still land. <button type="button" class="confirm-live-checked-btn" data-attempt-id="' + esc(b.attemptId || '') + '" data-id="' + esc(b.proposalId) + '">Confirm live checked</button></div>';
|
|
4323
|
+
}).join('');
|
|
4324
|
+
}
|
|
4325
|
+
|
|
4179
4326
|
function renderProposalsList() {
|
|
4180
4327
|
var list = $('proposals-list');
|
|
4181
4328
|
if (!list) return;
|
|
4329
|
+
var blockBanner = document.getElementById('deploy-unknown-blocks');
|
|
4330
|
+
if (!blockBanner && list.parentNode) {
|
|
4331
|
+
blockBanner = document.createElement('div');
|
|
4332
|
+
blockBanner.id = 'deploy-unknown-blocks';
|
|
4333
|
+
list.parentNode.insertBefore(blockBanner, list);
|
|
4334
|
+
}
|
|
4335
|
+
if (blockBanner) {
|
|
4336
|
+
blockBanner.innerHTML = renderDeployUnknownBlocks(deployUnknownBlocks);
|
|
4337
|
+
bindProposalActions(blockBanner);
|
|
4338
|
+
}
|
|
4182
4339
|
var filters = $('proposals-filters');
|
|
4183
4340
|
var pending = cachedProposals.filter(function (p) { return (p.status === 'pending' || p.status === 'conflict'); });
|
|
4184
4341
|
var decided = cachedProposals.filter(function (p) { return (p.status !== 'pending' && p.status !== 'conflict'); });
|
|
@@ -4232,7 +4389,7 @@ ${os}
|
|
|
4232
4389
|
bindProposalActions(list);
|
|
4233
4390
|
focusProposalFromHash();
|
|
4234
4391
|
}
|
|
4235
|
-
`}a(
|
|
4392
|
+
`}a(Qg,"pageClientScriptPartB2CardsList");function ey(){return`
|
|
4236
4393
|
async function refreshFlowLabels() {
|
|
4237
4394
|
try {
|
|
4238
4395
|
var data = await api('/flows');
|
|
@@ -4267,6 +4424,7 @@ ${os}
|
|
|
4267
4424
|
decidedLoaded = decidedList.length;
|
|
4268
4425
|
decidedTotal = Number(decidedData && decidedData.total) || decidedList.length;
|
|
4269
4426
|
decidedHasMore = !!(decidedData && decidedData.hasMore);
|
|
4427
|
+
deployUnknownBlocks = (pendingData && pendingData.deployUnknownBlocks) || [];
|
|
4270
4428
|
cachedProposals = ((pendingData && pendingData.proposals) || []).concat(decidedList);
|
|
4271
4429
|
cachedProposals.forEach(function (p) {
|
|
4272
4430
|
var id = proposalFlowId(p);
|
|
@@ -4274,7 +4432,7 @@ ${os}
|
|
|
4274
4432
|
if (id && label) cachedFlowLabels[id] = String(label);
|
|
4275
4433
|
});
|
|
4276
4434
|
var sig = JSON.stringify([decidedHasMore, decidedTotal, cachedProposals.map(function (p) {
|
|
4277
|
-
return [p.id, p.status, p.updatedAt, p.
|
|
4435
|
+
return [p.id, p.status, p.updatedAt, p.baseRevChanged, p.flowLabel, p.title];
|
|
4278
4436
|
})]);
|
|
4279
4437
|
var unchanged = sameAsLastRender('proposals', sig);
|
|
4280
4438
|
if (opts.skipRender || (opts.auto && unchanged)) {
|
|
@@ -4407,7 +4565,7 @@ ${os}
|
|
|
4407
4565
|
replaceLocationHash('#proposals');
|
|
4408
4566
|
return true;
|
|
4409
4567
|
}
|
|
4410
|
-
`}a(
|
|
4568
|
+
`}a(ey,"pageClientScriptPartB2Focus");function ty(){return`
|
|
4411
4569
|
function bindDiffModeToggle(host, diff) {
|
|
4412
4570
|
host.querySelectorAll('[data-diff-mode]').forEach(function (btn) {
|
|
4413
4571
|
btn.addEventListener('click', function () {
|
|
@@ -4429,8 +4587,8 @@ ${os}
|
|
|
4429
4587
|
p.rebase = Object.assign({}, p.rebase || {}, diff.rebase);
|
|
4430
4588
|
p.status = diff.rebase.state === 'conflict' ? 'conflict' : 'pending';
|
|
4431
4589
|
}
|
|
4432
|
-
if (p && typeof diff.
|
|
4433
|
-
p.
|
|
4590
|
+
if (p && typeof diff.baseRevChanged === 'boolean') {
|
|
4591
|
+
p.baseRevChanged = diff.baseRevChanged;
|
|
4434
4592
|
}
|
|
4435
4593
|
// Move cards between review and conflict groups when the rebase state changes.
|
|
4436
4594
|
if (p && (p.status === 'pending' || p.status === 'conflict') && wasStale !== proposalIsStale(p)) {
|
|
@@ -4461,8 +4619,10 @@ ${os}
|
|
|
4461
4619
|
if (pill) { pill.className = 'status-pill ' + p.status; pill.textContent = p.status === 'conflict' ? 'Conflict' : p.status; }
|
|
4462
4620
|
var conflictHost = article.querySelector('.proposal-conflict-host');
|
|
4463
4621
|
if (conflictHost) conflictHost.innerHTML = renderConflictPanel(p, diff);
|
|
4464
|
-
var
|
|
4465
|
-
if (
|
|
4622
|
+
var actionHost = article.querySelector('.proposal-actions');
|
|
4623
|
+
if (actionHost) actionHost.innerHTML = renderPendingActions(p, proposalIsStale(p));
|
|
4624
|
+
var notes = article.querySelector('.proposal-review-notes');
|
|
4625
|
+
if (notes && !p.deployPending) notes.innerHTML = renderReviewWarnings(p, diff);
|
|
4466
4626
|
var chip = article.querySelector('.rebase-chip');
|
|
4467
4627
|
if (chip) chip.outerHTML = renderRebaseChip(p, diff);
|
|
4468
4628
|
bindProposalActions(article);
|
|
@@ -4472,7 +4632,7 @@ ${os}
|
|
|
4472
4632
|
|
|
4473
4633
|
// Banner for any large delete (stale or live) \u2014 owners must see the risk.
|
|
4474
4634
|
var existingBanner = article.querySelector('.delete-banner');
|
|
4475
|
-
if (isLargeDelete(diff)) {
|
|
4635
|
+
if (!proposalHasNoNetChange(diff) && isLargeDelete(diff)) {
|
|
4476
4636
|
var beforeN = diff.summary.beforeNodeCount || 0;
|
|
4477
4637
|
var removedN = diff.summary.removed || 0;
|
|
4478
4638
|
var html = 'Deletes ' + removedN + ' of ' + beforeN + ' nodes';
|
|
@@ -4531,11 +4691,11 @@ ${os}
|
|
|
4531
4691
|
}
|
|
4532
4692
|
} catch (e) {
|
|
4533
4693
|
if (host) {
|
|
4534
|
-
host.innerHTML = '<p class="muted">Could not load changes: ' +
|
|
4694
|
+
host.innerHTML = '<p class="muted" role="alert">Could not load changes: ' +
|
|
4535
4695
|
esc(e.message || String(e)) + '</p>';
|
|
4536
4696
|
}
|
|
4537
4697
|
}
|
|
4538
|
-
}`}a(
|
|
4698
|
+
}`}a(ty,"pageClientScriptPartB2Apply");function ny(){return`
|
|
4539
4699
|
|
|
4540
4700
|
function dialogFocusables(dialog) {
|
|
4541
4701
|
if (!dialog) return [];
|
|
@@ -4638,7 +4798,7 @@ ${os}
|
|
|
4638
4798
|
} else if (destructive) {
|
|
4639
4799
|
heading.textContent = 'Confirm approve & deploy';
|
|
4640
4800
|
} else if (warns.length) {
|
|
4641
|
-
heading.textContent = 'Approve with
|
|
4801
|
+
heading.textContent = 'Approve with review notes';
|
|
4642
4802
|
} else {
|
|
4643
4803
|
heading.textContent = 'Confirm approve & deploy';
|
|
4644
4804
|
}
|
|
@@ -4664,7 +4824,7 @@ ${os}
|
|
|
4664
4824
|
msg.className = 'danger-text';
|
|
4665
4825
|
msg.hidden = false;
|
|
4666
4826
|
} else if (warns.length) {
|
|
4667
|
-
msg.textContent = '
|
|
4827
|
+
msg.textContent = 'Review the notes below before deploying. Configuration gaps can be completed in the editor.';
|
|
4668
4828
|
msg.className = 'muted';
|
|
4669
4829
|
msg.hidden = false;
|
|
4670
4830
|
} else {
|
|
@@ -4678,9 +4838,7 @@ ${os}
|
|
|
4678
4838
|
if (warnEl) {
|
|
4679
4839
|
if (warns.length) {
|
|
4680
4840
|
warnEl.hidden = false;
|
|
4681
|
-
warnEl.innerHTML =
|
|
4682
|
-
title: 'Configuration warnings'
|
|
4683
|
-
});
|
|
4841
|
+
warnEl.innerHTML = renderReviewWarnings(p, diff);
|
|
4684
4842
|
} else {
|
|
4685
4843
|
warnEl.hidden = true;
|
|
4686
4844
|
warnEl.innerHTML = '';
|
|
@@ -4738,16 +4896,16 @@ ${os}
|
|
|
4738
4896
|
renderProposalsList();
|
|
4739
4897
|
}
|
|
4740
4898
|
|
|
4741
|
-
async function doApproveProposal(id, deploymentType) {
|
|
4899
|
+
async function doApproveProposal(id, deploymentType, overrideDeployOutcomeUnknown) {
|
|
4742
4900
|
proposalActionsInFlight += 1;
|
|
4743
4901
|
try {
|
|
4744
|
-
return await doApproveProposalInner(id, deploymentType);
|
|
4902
|
+
return await doApproveProposalInner(id, deploymentType, overrideDeployOutcomeUnknown);
|
|
4745
4903
|
} finally {
|
|
4746
4904
|
proposalActionsInFlight -= 1;
|
|
4747
4905
|
}
|
|
4748
4906
|
}
|
|
4749
4907
|
|
|
4750
|
-
async function doApproveProposalInner(id, deploymentType) {
|
|
4908
|
+
async function doApproveProposalInner(id, deploymentType, overrideDeployOutcomeUnknown) {
|
|
4751
4909
|
var flash = $('proposals-flash');
|
|
4752
4910
|
var pBefore = cachedProposals.filter(function (x) { return x.id === id; })[0];
|
|
4753
4911
|
var toastTitle = proposalToastTitle(pBefore);
|
|
@@ -4758,9 +4916,11 @@ ${os}
|
|
|
4758
4916
|
}).length
|
|
4759
4917
|
: 0;
|
|
4760
4918
|
try {
|
|
4919
|
+
var approveBody = { deploymentType: deploymentType, mergedHash: proposalDiffCache[id] && proposalDiffCache[id].rebase && proposalDiffCache[id].rebase.mergedHash };
|
|
4920
|
+
if (overrideDeployOutcomeUnknown === true) approveBody.overrideDeployOutcomeUnknown = true;
|
|
4761
4921
|
var result = await api('/proposals/' + encodeURIComponent(id) + '/approve', {
|
|
4762
4922
|
method: 'POST',
|
|
4763
|
-
body: JSON.stringify(
|
|
4923
|
+
body: JSON.stringify(approveBody)
|
|
4764
4924
|
});
|
|
4765
4925
|
var viewOpts = { title: toastTitle, href: proposalViewHref(id), linkLabel: 'View' };
|
|
4766
4926
|
if (result.status === 'rejected') {
|
|
@@ -4782,7 +4942,7 @@ ${os}
|
|
|
4782
4942
|
cachedProposals.forEach(function (peer) {
|
|
4783
4943
|
if (peer.id !== id && (peer.status === 'pending' || peer.status === 'conflict') && peer.expectedRev === expectedRev) {
|
|
4784
4944
|
delete proposalDiffCache[peer.id];
|
|
4785
|
-
peer.
|
|
4945
|
+
peer.baseRevChanged = true;
|
|
4786
4946
|
}
|
|
4787
4947
|
});
|
|
4788
4948
|
await loadProposals({ skipRender: true });
|
|
@@ -4800,6 +4960,15 @@ ${os}
|
|
|
4800
4960
|
loadLogs();
|
|
4801
4961
|
} catch (e) {
|
|
4802
4962
|
var code = e.body && e.body.code;
|
|
4963
|
+
if (code === 'deploy_outcome_unknown' && !overrideDeployOutcomeUnknown) {
|
|
4964
|
+
openConfirmDialog({
|
|
4965
|
+
title: 'Deployment outcome unknown',
|
|
4966
|
+
message: 'An earlier deployment can still overwrite this approval. Override the safety block and deploy anyway?',
|
|
4967
|
+
confirmLabel: 'Override & deploy', danger: true,
|
|
4968
|
+
onConfirm: function () { return doApproveProposal(id, deploymentType, true); }
|
|
4969
|
+
});
|
|
4970
|
+
return;
|
|
4971
|
+
}
|
|
4803
4972
|
var errMsg = e.message || String(e);
|
|
4804
4973
|
if (e.status === 409 && (code === 'rebase_ack_required' || code === 'proposal_conflict')) {
|
|
4805
4974
|
errMsg = code === 'rebase_ack_required'
|
|
@@ -4830,6 +4999,7 @@ ${os}
|
|
|
4830
4999
|
showToast('Review the loaded diff, then click Approve again.', false);
|
|
4831
5000
|
return;
|
|
4832
5001
|
}
|
|
5002
|
+
if (proposalHasNoNetChange(diff)) { showToast('Nothing changes in the live flow. Reject this proposal to close it.', true); return; }
|
|
4833
5003
|
if (needsApproveConfirm(diff, p)) {
|
|
4834
5004
|
openDeleteConfirmDialog(id, deploymentType, diff);
|
|
4835
5005
|
return;
|
|
@@ -4856,13 +5026,14 @@ ${os}
|
|
|
4856
5026
|
var pBefore = cachedProposals.filter(function (x) { return x.id === id; })[0];
|
|
4857
5027
|
var toastTitle = proposalToastTitle(pBefore);
|
|
4858
5028
|
try {
|
|
4859
|
-
await api('/proposals/' + encodeURIComponent(id) + '/reject', {
|
|
5029
|
+
var rejected = await api('/proposals/' + encodeURIComponent(id) + '/reject', {
|
|
4860
5030
|
method: 'POST',
|
|
4861
5031
|
body: JSON.stringify({ reason: reason })
|
|
4862
5032
|
});
|
|
4863
5033
|
delete proposalDiffCache[id];
|
|
4864
5034
|
if (!silent) {
|
|
4865
5035
|
var okMsg = dismissed ? 'Proposal dismissed.' : 'Proposal rejected.';
|
|
5036
|
+
if (rejected.rejectedWhileDeployUnknown) okMsg += ' Warning: an earlier deploy may still land.';
|
|
4866
5037
|
showFlash(flash, okMsg + (toastTitle ? ' \u2014 ' + toastTitle : ''), true);
|
|
4867
5038
|
showToast(okMsg, true, {
|
|
4868
5039
|
title: toastTitle,
|
|
@@ -4883,7 +5054,7 @@ ${os}
|
|
|
4883
5054
|
throw e;
|
|
4884
5055
|
}
|
|
4885
5056
|
}
|
|
4886
|
-
`}a(
|
|
5057
|
+
`}a(ny,"pageClientScriptPartC");function ry(){return`
|
|
4887
5058
|
var cachedLogs = [];
|
|
4888
5059
|
|
|
4889
5060
|
function renderLogsTable(logs) {
|
|
@@ -4965,7 +5136,7 @@ ${os}
|
|
|
4965
5136
|
showFlash(flash, e.message || String(e), false);
|
|
4966
5137
|
}
|
|
4967
5138
|
}
|
|
4968
|
-
`}a(
|
|
5139
|
+
`}a(ry,"pageClientScriptPartCLogs");function oy(){return`
|
|
4969
5140
|
function applyStatus(s) {
|
|
4970
5141
|
aiOn = !!s.aiFeaturesEnabled;
|
|
4971
5142
|
canManage = !!(s.capabilities && s.capabilities.manageLinks);
|
|
@@ -5028,8 +5199,8 @@ ${os}
|
|
|
5028
5199
|
}
|
|
5029
5200
|
|
|
5030
5201
|
$('proposals-hint').textContent = canApprove
|
|
5031
|
-
? 'Review
|
|
5032
|
-
: '
|
|
5202
|
+
? 'Review your AI assistant\u2019s changes. Approve & deploy applies them to Node-RED. Reject closes a proposal without changing the flow.'
|
|
5203
|
+
: 'Review proposals here. A full Node-RED admin can approve or reject them.';
|
|
5033
5204
|
|
|
5034
5205
|
renderLinksManage();
|
|
5035
5206
|
}
|
|
@@ -5038,10 +5209,14 @@ ${os}
|
|
|
5038
5209
|
var nav = $('main-nav');
|
|
5039
5210
|
if (!nav) return;
|
|
5040
5211
|
function updateStuck() {
|
|
5041
|
-
var
|
|
5212
|
+
var rect = nav.getBoundingClientRect();
|
|
5213
|
+
if (rect.height > 0) document.documentElement.style.setProperty('--nav-height', (rect.height + 20) + 'px');
|
|
5214
|
+
var top = rect.top;
|
|
5042
5215
|
nav.classList.toggle('is-stuck', top <= 0);
|
|
5043
5216
|
}
|
|
5044
5217
|
window.addEventListener('scroll', updateStuck, { passive: true });
|
|
5218
|
+
window.addEventListener('resize', updateStuck);
|
|
5219
|
+
if (typeof ResizeObserver !== 'undefined') new ResizeObserver(updateStuck).observe(nav);
|
|
5045
5220
|
updateStuck();
|
|
5046
5221
|
}
|
|
5047
5222
|
|
|
@@ -5126,7 +5301,7 @@ ${os}
|
|
|
5126
5301
|
});
|
|
5127
5302
|
}
|
|
5128
5303
|
}
|
|
5129
|
-
`}a(
|
|
5304
|
+
`}a(oy,"pageClientScriptPartD");function sy(){return`
|
|
5130
5305
|
async function boot() {
|
|
5131
5306
|
accessToken = readEditorToken();
|
|
5132
5307
|
if (!accessToken) {
|
|
@@ -5146,7 +5321,7 @@ ${os}
|
|
|
5146
5321
|
setupStickyNav();
|
|
5147
5322
|
// Restore tab from hash (or default #status); keep #proposal=<id> until focus consumes it.
|
|
5148
5323
|
var initialTab = initialTabFromHash(location.hash || '');
|
|
5149
|
-
selectTab(initialTab, { skipHash: !!readProposalHashId() });
|
|
5324
|
+
selectTab(initialTab, { skipHash: !!readProposalHashId(), focusHeading: !!location.hash });
|
|
5150
5325
|
if (initialTab === 'settings') loadSettings();
|
|
5151
5326
|
if (initialTab === 'oauth') loadOAuthTab();
|
|
5152
5327
|
if (aiOn) {
|
|
@@ -5218,23 +5393,28 @@ ${os}
|
|
|
5218
5393
|
|
|
5219
5394
|
setupDialogs();
|
|
5220
5395
|
|
|
5221
|
-
|
|
5396
|
+
function routeFromLocation() {
|
|
5397
|
+
if (authGateShown) return;
|
|
5398
|
+
// A history traversal may emit both popstate and hashchange for the same URL.
|
|
5399
|
+
if (selectedLocationHash === String(location.hash || '')) return;
|
|
5222
5400
|
var proposalId = readProposalHashId();
|
|
5223
5401
|
if (proposalId) {
|
|
5224
|
-
selectTab('proposals', { skipHash: true });
|
|
5402
|
+
selectTab('proposals', { skipHash: true, focusHeading: true });
|
|
5225
5403
|
loadTabData('proposals');
|
|
5226
5404
|
return;
|
|
5227
5405
|
}
|
|
5228
5406
|
var tab = tabFromHash(location.hash) || 'status';
|
|
5229
|
-
selectTab(tab, { skipHash: true });
|
|
5407
|
+
selectTab(tab, { skipHash: true, focusHeading: true });
|
|
5230
5408
|
loadTabData(tab);
|
|
5231
|
-
}
|
|
5409
|
+
}
|
|
5410
|
+
window.addEventListener('hashchange', routeFromLocation);
|
|
5411
|
+
window.addEventListener('popstate', routeFromLocation);
|
|
5232
5412
|
|
|
5233
|
-
${
|
|
5234
|
-
${
|
|
5413
|
+
${iy()}
|
|
5414
|
+
${ay()}
|
|
5235
5415
|
boot();
|
|
5236
5416
|
})();
|
|
5237
|
-
`}a(
|
|
5417
|
+
`}a(sy,"pageClientScriptPartDBoot");function iy(){return`
|
|
5238
5418
|
/*
|
|
5239
5419
|
* Jump links to the editor (#flow/<id>, #node/<id>). A click first asks an
|
|
5240
5420
|
* already-open editor tab (BroadcastChannel 'node-red-ai-access', answered by
|
|
@@ -5299,7 +5479,7 @@ ${qg()}
|
|
|
5299
5479
|
ev.stopPropagation();
|
|
5300
5480
|
revealInEditor(a.getAttribute('data-reveal-kind'), a.getAttribute('data-reveal-id'), a.getAttribute('href'));
|
|
5301
5481
|
});
|
|
5302
|
-
`}a(
|
|
5482
|
+
`}a(iy,"pageClientEditorJumpScript");function ay(){return`
|
|
5303
5483
|
/*
|
|
5304
5484
|
* Auto-refresh when a tab left open becomes visible / focused again.
|
|
5305
5485
|
* Throttled; skipped while a dialog (e.g. the one-time token modal) is open or
|
|
@@ -5329,7 +5509,7 @@ ${qg()}
|
|
|
5329
5509
|
var s = await api('/status');
|
|
5330
5510
|
if (!isLatestLoad('status', gen)) return;
|
|
5331
5511
|
applyStatus(s);
|
|
5332
|
-
var sel = document.querySelector('[
|
|
5512
|
+
var sel = document.querySelector('nav.tabs button[aria-current="page"]');
|
|
5333
5513
|
var tab = sel ? sel.getAttribute('data-tab') : '';
|
|
5334
5514
|
if (aiOn) {
|
|
5335
5515
|
if (canManage) loadLinks({ auto: true });
|
|
@@ -5349,7 +5529,7 @@ ${qg()}
|
|
|
5349
5529
|
});
|
|
5350
5530
|
window.addEventListener('focus', autoRefresh);
|
|
5351
5531
|
|
|
5352
|
-
`}a(
|
|
5532
|
+
`}a(ay,"pageClientAutoRefreshScript");function Ic(){return(Hg()+jg()+Ug()+Bg()+qg()+Wg()+zg()+Gg()+Kg()+Jg()+Vg()+Yg()+Zg()+Xg()+Qg()+ey()+ty()+ny()+ry()+oy()+Ec()+sy()).trim()}a(Ic,"pageClientScript");y();function dy(){return`
|
|
5353
5533
|
:root {
|
|
5354
5534
|
color-scheme: light;
|
|
5355
5535
|
--bg: #eef2f5;
|
|
@@ -5377,10 +5557,12 @@ body {
|
|
|
5377
5557
|
linear-gradient(165deg, #e8eef3 0%, var(--bg) 55%, #e4ebe6 100%);
|
|
5378
5558
|
color: var(--ink);
|
|
5379
5559
|
min-height: 100vh;
|
|
5560
|
+
font-size: 17px;
|
|
5561
|
+
line-height: 1.55;
|
|
5380
5562
|
}
|
|
5381
5563
|
main {
|
|
5382
5564
|
width: 100%;
|
|
5383
|
-
max-width:
|
|
5565
|
+
max-width: 1200px;
|
|
5384
5566
|
margin: 0 auto;
|
|
5385
5567
|
padding: 2.25rem clamp(0.85rem, 2.5vw, 1.75rem) 3.5rem;
|
|
5386
5568
|
}
|
|
@@ -5395,21 +5577,10 @@ h1 {
|
|
|
5395
5577
|
line-height: 1.5;
|
|
5396
5578
|
margin: 0 0 0.85rem;
|
|
5397
5579
|
}
|
|
5398
|
-
.how-it-works {
|
|
5399
|
-
margin: 0 0 1.25rem;
|
|
5400
|
-
padding: 0.75rem 0.9rem 0.75rem 1.6rem;
|
|
5401
|
-
background: var(--accent-soft);
|
|
5402
|
-
border-radius: 8px;
|
|
5403
|
-
color: var(--ink);
|
|
5404
|
-
line-height: 1.45;
|
|
5405
|
-
font-size: 0.95rem;
|
|
5406
|
-
}
|
|
5407
|
-
.how-it-works li { margin: 0.2rem 0; }
|
|
5408
|
-
.how-it-works strong { color: var(--accent); }
|
|
5409
5580
|
/* Signed-out card: same look as the OAuth consent / sign-in page (light only). */
|
|
5410
5581
|
.auth-gate {
|
|
5411
5582
|
--sg-bg: #f3f4f6; --sg-card: #fff; --sg-text: #111827; --sg-muted: #4b5563; --sg-line: #e5e7eb;
|
|
5412
|
-
--sg-soft: #f9fafb; --sg-brand: #
|
|
5583
|
+
--sg-soft: #f9fafb; --sg-brand: #0a5c45; --sg-brand-2: #084b38; --sg-focus: #2563eb;
|
|
5413
5584
|
color-scheme: light;
|
|
5414
5585
|
position: fixed; inset: 0; z-index: 1000; overflow: auto;
|
|
5415
5586
|
display: flex; align-items: center; justify-content: center; padding: 24px 16px;
|
|
@@ -5428,7 +5599,7 @@ body.signed-out { overflow: hidden; }
|
|
|
5428
5599
|
.auth-gate h1 { font-size: 19px; line-height: 1.3; margin: 0 0 6px; color: var(--sg-text); }
|
|
5429
5600
|
.auth-gate .sg-sub { margin: 0 0 14px; color: var(--sg-muted); font-size: 14px; }
|
|
5430
5601
|
.auth-gate .sg-status { font-size: 14px; color: var(--sg-muted); margin: 0 0 14px; }
|
|
5431
|
-
.auth-gate .sg-status.error { color: var(--
|
|
5602
|
+
.auth-gate .sg-status.error { color: var(--danger); }
|
|
5432
5603
|
.auth-gate .sg-actions { display: flex; gap: 10px; }
|
|
5433
5604
|
.auth-gate button { font: inherit; border-radius: 10px; padding: 10px 16px; cursor: pointer; border: 1px solid var(--sg-line); background: var(--sg-card); color: var(--sg-text); }
|
|
5434
5605
|
.auth-gate button:focus-visible, .auth-gate input:focus-visible, .auth-gate a:focus-visible { outline: 3px solid var(--sg-focus); outline-offset: 2px; }
|
|
@@ -5441,41 +5612,74 @@ body.signed-out { overflow: hidden; }
|
|
|
5441
5612
|
.auth-gate .sg-login input { font: inherit; padding: 9px 11px; border-radius: 10px; border: 1px solid var(--sg-line); background: var(--sg-soft); color: var(--sg-text); }
|
|
5442
5613
|
.auth-gate .sg-editor-option { display: inline-block; margin: 10px 0 14px; font-size: 14px; }
|
|
5443
5614
|
.auth-gate .sg-editor-option[hidden] { display: none; }
|
|
5444
|
-
.auth-gate .sg-foot { padding: 12px 22px; border-top: 1px solid var(--sg-line); font-size:
|
|
5615
|
+
.auth-gate .sg-foot { padding: 12px 22px; border-top: 1px solid var(--sg-line); font-size: 14px; font-weight: 500; color: var(--sg-text); background: var(--sg-soft); }
|
|
5445
5616
|
.auth-gate a { color: var(--sg-focus); }
|
|
5446
5617
|
@media (max-width: 420px) { .auth-gate .sg-actions { flex-direction: column-reverse; } .auth-gate .sg-secondary { flex: 1; } }
|
|
5447
|
-
`}a(
|
|
5618
|
+
`}a(dy,"pageStylesPartA");function ly(){return`
|
|
5619
|
+
/* Layout uses the existing palette, including any host theme variable overrides. */
|
|
5620
|
+
.settings-layout { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 2rem; align-items: start; }
|
|
5448
5621
|
nav.tabs {
|
|
5449
5622
|
position: sticky;
|
|
5450
|
-
top:
|
|
5451
|
-
z-index: 40;
|
|
5623
|
+
top: 1rem;
|
|
5452
5624
|
display: flex;
|
|
5453
|
-
flex-
|
|
5454
|
-
gap:
|
|
5455
|
-
|
|
5456
|
-
padding: 0.
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5625
|
+
flex-direction: column;
|
|
5626
|
+
gap: 1rem;
|
|
5627
|
+
min-width: 0;
|
|
5628
|
+
padding: 0.25rem 0;
|
|
5629
|
+
}
|
|
5630
|
+
.nav-group-title {
|
|
5631
|
+
margin: 0 0 0.4rem;
|
|
5632
|
+
padding: 0 0.85rem;
|
|
5633
|
+
font-size: 0.75rem;
|
|
5634
|
+
font-weight: 600;
|
|
5635
|
+
letter-spacing: 0.06em;
|
|
5636
|
+
text-transform: uppercase;
|
|
5637
|
+
color: var(--muted);
|
|
5465
5638
|
}
|
|
5466
5639
|
nav.tabs button {
|
|
5467
|
-
|
|
5468
|
-
|
|
5640
|
+
position: relative;
|
|
5641
|
+
display: flex;
|
|
5642
|
+
align-items: center;
|
|
5643
|
+
gap: 0.6rem;
|
|
5644
|
+
width: 100%;
|
|
5645
|
+
border: 0;
|
|
5646
|
+
background: transparent;
|
|
5469
5647
|
color: var(--ink);
|
|
5470
|
-
padding: 0.
|
|
5471
|
-
border-radius:
|
|
5648
|
+
padding: 0.5rem 0.85rem;
|
|
5649
|
+
border-radius: 6px;
|
|
5472
5650
|
cursor: pointer;
|
|
5473
5651
|
font: inherit;
|
|
5652
|
+
text-align: left;
|
|
5474
5653
|
}
|
|
5475
|
-
nav.tabs button
|
|
5654
|
+
nav.tabs button svg { flex: 0 0 16px; }
|
|
5655
|
+
nav.tabs button:hover { background: var(--soft); }
|
|
5656
|
+
nav.tabs button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
5657
|
+
nav.tabs button[aria-current="page"] { background: var(--accent-soft); font-weight: 600; }
|
|
5658
|
+
nav.tabs button[aria-current="page"]::before {
|
|
5659
|
+
content: "";
|
|
5660
|
+
position: absolute;
|
|
5661
|
+
left: 0;
|
|
5662
|
+
top: 0.45rem;
|
|
5663
|
+
bottom: 0.45rem;
|
|
5664
|
+
width: 3px;
|
|
5665
|
+
border-radius: 2px;
|
|
5476
5666
|
background: var(--accent);
|
|
5477
|
-
|
|
5478
|
-
|
|
5667
|
+
}
|
|
5668
|
+
.settings-layout > section.panel { grid-column: 2; grid-row: 1; min-width: 0; }
|
|
5669
|
+
section.panel > h2 {
|
|
5670
|
+
padding-bottom: 0.65rem;
|
|
5671
|
+
border-bottom: 1px solid var(--line);
|
|
5672
|
+
font-size: 1.65rem;
|
|
5673
|
+
font-weight: 500;
|
|
5674
|
+
}
|
|
5675
|
+
section.panel > h3 { margin: 1.4rem 0 0.75rem; font-size: 1.15rem; font-weight: 600; }
|
|
5676
|
+
.settings-subpart > h3 { margin: 1.4rem 0 0.75rem; font-size: 1.15rem; }
|
|
5677
|
+
@media (max-width: 768px) {
|
|
5678
|
+
.settings-layout { grid-template-columns: minmax(0, 1fr); gap: 1rem; }
|
|
5679
|
+
.settings-layout > section.panel { grid-column: 1; grid-row: 2; padding: 1rem; }
|
|
5680
|
+
nav.tabs { position: static; flex-direction: column; gap: 0.75rem; }
|
|
5681
|
+
.nav-group { width: 100%; min-width: 0; }
|
|
5682
|
+
nav.tabs button { font-size: 0.95rem; }
|
|
5479
5683
|
}
|
|
5480
5684
|
.tab-badge {
|
|
5481
5685
|
display: inline-block;
|
|
@@ -5486,7 +5690,7 @@ nav.tabs button[aria-selected="true"] {
|
|
|
5486
5690
|
border-radius: 999px;
|
|
5487
5691
|
background: var(--card);
|
|
5488
5692
|
color: var(--accent);
|
|
5489
|
-
font-size: 0.
|
|
5693
|
+
font-size: 0.85rem;
|
|
5490
5694
|
font-weight: 700;
|
|
5491
5695
|
line-height: 1.25;
|
|
5492
5696
|
vertical-align: middle;
|
|
@@ -5494,7 +5698,7 @@ nav.tabs button[aria-selected="true"] {
|
|
|
5494
5698
|
overflow: hidden;
|
|
5495
5699
|
text-overflow: ellipsis;
|
|
5496
5700
|
}
|
|
5497
|
-
nav.tabs button[aria-
|
|
5701
|
+
nav.tabs button[aria-current="page"] .tab-badge {
|
|
5498
5702
|
background: rgba(255, 255, 255, 0.92);
|
|
5499
5703
|
color: var(--accent);
|
|
5500
5704
|
}
|
|
@@ -5506,9 +5710,9 @@ section.panel {
|
|
|
5506
5710
|
padding: 1.15rem 1.25rem 1.35rem;
|
|
5507
5711
|
}
|
|
5508
5712
|
section.panel.active { display: block; }
|
|
5509
|
-
section.panel h2 {
|
|
5713
|
+
section.panel > h2 {
|
|
5510
5714
|
margin: 0 0 0.75rem;
|
|
5511
|
-
font-size: 1.
|
|
5715
|
+
font-size: 1.65rem;
|
|
5512
5716
|
}
|
|
5513
5717
|
.warning {
|
|
5514
5718
|
background: var(--warn-bg);
|
|
@@ -5521,11 +5725,12 @@ section.panel h2 {
|
|
|
5521
5725
|
.warning p { margin: 0; line-height: 1.45; }
|
|
5522
5726
|
dl.status {
|
|
5523
5727
|
display: grid;
|
|
5524
|
-
grid-template-columns:
|
|
5525
|
-
gap: 0.
|
|
5728
|
+
grid-template-columns: minmax(0, 1fr);
|
|
5729
|
+
gap: 0.25rem;
|
|
5526
5730
|
margin: 0;
|
|
5527
5731
|
}
|
|
5528
5732
|
dt { color: var(--muted); }
|
|
5733
|
+
dl.status dd { margin-bottom: 0.75rem; }
|
|
5529
5734
|
dd {
|
|
5530
5735
|
margin: 0;
|
|
5531
5736
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
@@ -5538,7 +5743,7 @@ dd {
|
|
|
5538
5743
|
padding: 0.22rem 0.55rem;
|
|
5539
5744
|
border-radius: 6px;
|
|
5540
5745
|
cursor: help;
|
|
5541
|
-
background: var`}a(
|
|
5746
|
+
background: var`}a(ly,"pageStylesPartA2");function cy(){return`(--accent-soft);
|
|
5542
5747
|
color: var(--accent);
|
|
5543
5748
|
font-size: 0.82rem;
|
|
5544
5749
|
font-weight: 650;
|
|
@@ -5575,7 +5780,7 @@ dd {
|
|
|
5575
5780
|
display: inline-block;
|
|
5576
5781
|
padding: 0.12rem 0.45rem;
|
|
5577
5782
|
border-radius: 4px;
|
|
5578
|
-
font-size: 0.
|
|
5783
|
+
font-size: 0.88rem;
|
|
5579
5784
|
font-weight: 650;
|
|
5580
5785
|
white-space: nowrap;
|
|
5581
5786
|
background: var(--soft);
|
|
@@ -5729,7 +5934,7 @@ dialog.token-modal { width: min(720px, 94vw); max-width: min(720px, 94vw); max-h
|
|
|
5729
5934
|
color: var(--muted);
|
|
5730
5935
|
font-size: 0.92rem;
|
|
5731
5936
|
}
|
|
5732
|
-
`}a(
|
|
5937
|
+
`}a(cy,"pageStylesPartB");function uy(){return`
|
|
5733
5938
|
.logs-toolbar { margin-bottom: 0.65rem; }
|
|
5734
5939
|
table.audit-table {
|
|
5735
5940
|
table-layout: fixed;
|
|
@@ -5768,7 +5973,7 @@ table.audit-table {
|
|
|
5768
5973
|
display: inline-block;
|
|
5769
5974
|
padding: 0.12rem 0.45rem;
|
|
5770
5975
|
border-radius: 5px;
|
|
5771
|
-
font-size: 0.
|
|
5976
|
+
font-size: 0.88rem;
|
|
5772
5977
|
font-weight: 650;
|
|
5773
5978
|
letter-spacing: 0.01em;
|
|
5774
5979
|
white-space: nowrap;
|
|
@@ -5795,7 +6000,7 @@ table.audit-table {
|
|
|
5795
6000
|
word-break: break-word;
|
|
5796
6001
|
max-height: 10rem;
|
|
5797
6002
|
overflow: auto;
|
|
5798
|
-
font-size: 0.
|
|
6003
|
+
font-size: 0.88rem;
|
|
5799
6004
|
background: var(--soft);
|
|
5800
6005
|
padding: 0.4rem 0.5rem;
|
|
5801
6006
|
border-radius: 6px;
|
|
@@ -5805,7 +6010,7 @@ table.audit-table {
|
|
|
5805
6010
|
cursor: help;
|
|
5806
6011
|
border-bottom: 1px dotted var(--line);
|
|
5807
6012
|
}
|
|
5808
|
-
`}a(
|
|
6013
|
+
`}a(uy,"pageStylesPartC");function py(){return`
|
|
5809
6014
|
.proposal-diff {
|
|
5810
6015
|
margin: 0.65rem 0;
|
|
5811
6016
|
padding: 0.55rem 0.65rem;
|
|
@@ -5873,7 +6078,7 @@ table.audit-table {
|
|
|
5873
6078
|
.prop-change { margin: 0.25rem 0 0.35rem; font-size: 0.88rem; }
|
|
5874
6079
|
.prop-change-scalar.split {
|
|
5875
6080
|
display: grid;
|
|
5876
|
-
grid-template-columns: 1fr auto 1fr;
|
|
6081
|
+
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
|
5877
6082
|
gap: 0.45rem;
|
|
5878
6083
|
align-items: start;
|
|
5879
6084
|
}
|
|
@@ -5882,15 +6087,15 @@ table.audit-table {
|
|
|
5882
6087
|
}
|
|
5883
6088
|
.prop-change-scalar .scalar-side .side-label {
|
|
5884
6089
|
display: block;
|
|
5885
|
-
font-size: 0.
|
|
6090
|
+
font-size: 0.88rem;
|
|
5886
6091
|
color: var(--muted);
|
|
5887
6092
|
margin-bottom: 0.15rem;
|
|
5888
6093
|
}
|
|
5889
|
-
`}a(
|
|
6094
|
+
`}a(py,"pageStylesPartD");function fy(){return`
|
|
5890
6095
|
.diff-block {
|
|
5891
6096
|
margin-top: 0.25rem;
|
|
5892
6097
|
font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
|
|
5893
|
-
font-size: 0.
|
|
6098
|
+
font-size: 0.88rem;
|
|
5894
6099
|
line-height: 1.4;
|
|
5895
6100
|
overflow-x: hidden;
|
|
5896
6101
|
overflow-y: auto;
|
|
@@ -5933,7 +6138,7 @@ table.audit-table {
|
|
|
5933
6138
|
.diff-blank { background: #f0f3f6; color: transparent; }
|
|
5934
6139
|
.diff-split {
|
|
5935
6140
|
display: grid;
|
|
5936
|
-
grid-template-columns: 1fr 1fr;
|
|
6141
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
5937
6142
|
gap: 0;
|
|
5938
6143
|
border: 1px solid var(--line);
|
|
5939
6144
|
border-radius: 5px;
|
|
@@ -5946,7 +6151,7 @@ table.audit-table {
|
|
|
5946
6151
|
overflow-y: auto;
|
|
5947
6152
|
max-height: 18rem;
|
|
5948
6153
|
font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
|
|
5949
|
-
font-size: 0.
|
|
6154
|
+
font-size: 0.88rem;
|
|
5950
6155
|
line-height: 1.4;
|
|
5951
6156
|
background: var(--soft);
|
|
5952
6157
|
}
|
|
@@ -5959,7 +6164,7 @@ table.audit-table {
|
|
|
5959
6164
|
z-index: 1;
|
|
5960
6165
|
background: #eef2f5;
|
|
5961
6166
|
color: var(--muted);
|
|
5962
|
-
font-size: 0.
|
|
6167
|
+
font-size: 0.85rem;
|
|
5963
6168
|
font-weight: 650;
|
|
5964
6169
|
letter-spacing: 0.02em;
|
|
5965
6170
|
text-transform: uppercase;
|
|
@@ -5968,9 +6173,9 @@ table.audit-table {
|
|
|
5968
6173
|
}
|
|
5969
6174
|
.diff-before { color: var(--danger); word-break: break-word; }
|
|
5970
6175
|
.diff-after { color: var(--ok); word-break: break-word; }
|
|
5971
|
-
`}a(
|
|
6176
|
+
`}a(fy,"pageStylesPartE");function hy(){return`
|
|
5972
6177
|
.proposal-detail {
|
|
5973
|
-
scroll-margin-top:
|
|
6178
|
+
scroll-margin-top: var(--nav-height, 6rem);
|
|
5974
6179
|
}
|
|
5975
6180
|
.proposal-detail.proposal-highlight {
|
|
5976
6181
|
outline: 2px solid var(--accent);
|
|
@@ -6062,7 +6267,7 @@ table.audit-table {
|
|
|
6062
6267
|
overflow: auto;
|
|
6063
6268
|
max-height: 14rem;
|
|
6064
6269
|
font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
|
|
6065
|
-
font-size: 0.
|
|
6270
|
+
font-size: 0.88rem;
|
|
6066
6271
|
line-height: 1.4;
|
|
6067
6272
|
background: var(--card);
|
|
6068
6273
|
border: 1px solid var(--line);
|
|
@@ -6094,18 +6299,16 @@ table.audit-table {
|
|
|
6094
6299
|
.audit-table .col-action { width: 5.5rem; min-width: 5.5rem; }
|
|
6095
6300
|
.audit-table .col-actor { width: 7rem; min-width: 6rem; }
|
|
6096
6301
|
}
|
|
6097
|
-
`}a(
|
|
6302
|
+
`}a(hy,"pageStylesPartF");function my(){return`
|
|
6098
6303
|
.dash-grid {
|
|
6099
6304
|
display: grid;
|
|
6100
|
-
grid-template-columns:
|
|
6305
|
+
grid-template-columns: minmax(0, 1fr);
|
|
6101
6306
|
gap: 0.65rem;
|
|
6102
6307
|
margin: 0 0 1rem;
|
|
6103
6308
|
}
|
|
6104
6309
|
.dash-card {
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
border-radius: 9px;
|
|
6108
|
-
padding: 0.75rem 0.85rem;
|
|
6310
|
+
border-bottom: 1px solid var(--line);
|
|
6311
|
+
padding: 0.75rem 0;
|
|
6109
6312
|
}
|
|
6110
6313
|
.dash-card-clickable { cursor: pointer; }
|
|
6111
6314
|
.dash-card-clickable:hover { border-color: var(--accent); background: var(--accent-soft); }
|
|
@@ -6122,7 +6325,8 @@ table.audit-table {
|
|
|
6122
6325
|
}
|
|
6123
6326
|
.filter-row {
|
|
6124
6327
|
display: flex;
|
|
6125
|
-
flex-wrap:
|
|
6328
|
+
flex-wrap: nowrap;
|
|
6329
|
+
overflow-x: auto;
|
|
6126
6330
|
gap: 0.4rem;
|
|
6127
6331
|
margin: 0 0 0.75rem;
|
|
6128
6332
|
align-items: center;
|
|
@@ -6326,7 +6530,7 @@ button.id-copy:focus {
|
|
|
6326
6530
|
background: var(--code-bg, var(--soft));
|
|
6327
6531
|
border-radius: 4px;
|
|
6328
6532
|
}
|
|
6329
|
-
`}a(
|
|
6533
|
+
`}a(my,"pageStylesPartG");function gy(){return`
|
|
6330
6534
|
.stale-group {
|
|
6331
6535
|
margin: 1rem 0 0;
|
|
6332
6536
|
border: 1px solid var(--warn-border);
|
|
@@ -6472,7 +6676,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6472
6676
|
display: inline-block;
|
|
6473
6677
|
padding: 0.1rem 0.45rem;
|
|
6474
6678
|
border-radius: 5px;
|
|
6475
|
-
font-size: 0.
|
|
6679
|
+
font-size: 0.88rem;
|
|
6476
6680
|
font-weight: 650;
|
|
6477
6681
|
text-transform: capitalize;
|
|
6478
6682
|
}
|
|
@@ -6490,25 +6694,43 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6490
6694
|
margin-left: auto;
|
|
6491
6695
|
}
|
|
6492
6696
|
.debug-toggle input { width: auto; }
|
|
6493
|
-
`}a(
|
|
6697
|
+
`}a(gy,"pageStylesPartH");function yy(){return`
|
|
6494
6698
|
.card {
|
|
6495
6699
|
background: var(--card);
|
|
6496
|
-
border:
|
|
6497
|
-
border-
|
|
6498
|
-
padding:
|
|
6499
|
-
margin: 0
|
|
6700
|
+
border: 0;
|
|
6701
|
+
border-bottom: 1px solid var(--line);
|
|
6702
|
+
padding: 1.25rem 0;
|
|
6703
|
+
margin: 0;
|
|
6500
6704
|
}
|
|
6501
6705
|
.card h3 { margin: 0 0 0.75rem; font-size: 1.05rem; }
|
|
6502
6706
|
.card-head { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; margin-bottom: 0.5rem; }
|
|
6503
6707
|
.card-head h3 { margin: 0; }
|
|
6504
6708
|
.settings-grid {
|
|
6505
6709
|
display: grid;
|
|
6506
|
-
grid-template-columns:
|
|
6507
|
-
gap:
|
|
6710
|
+
grid-template-columns: minmax(0, 1fr);
|
|
6711
|
+
gap: 0;
|
|
6508
6712
|
align-items: start;
|
|
6509
|
-
margin-bottom:
|
|
6510
|
-
}
|
|
6511
|
-
.settings-grid .card { margin: 0; }
|
|
6713
|
+
margin-bottom: 0;
|
|
6714
|
+
}
|
|
6715
|
+
.settings-grid .card { margin: 0; min-width: 0; }
|
|
6716
|
+
/* Setting groups and form fields follow the same vertical reading order. */
|
|
6717
|
+
.settings-subpart { padding-bottom: 1.25rem; border-bottom: 1px solid var(--line); }
|
|
6718
|
+
section.panel:not(#section-proposals) .row { flex-direction: column; align-items: flex-start; }
|
|
6719
|
+
.logs-toolbar { padding-bottom: 1rem; border-bottom: 1px solid var(--line); }
|
|
6720
|
+
.logs-toolbar .debug-toggle { margin-left: 0; }
|
|
6721
|
+
section.panel { scroll-margin-top: 1rem; }
|
|
6722
|
+
.proposal-conflict, .conflict-choice { padding: 1rem; margin: 0.75rem 0; border: 1px solid var(--warn-border); border-radius: 8px; background: var(--warn-bg); }
|
|
6723
|
+
.conflict-option { margin: 0.8rem 0; }
|
|
6724
|
+
.conflict-values dl { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.75rem; }
|
|
6725
|
+
.conflict-values dl > div { min-width: 0; }
|
|
6726
|
+
.conflict-values dd { margin: 0; }
|
|
6727
|
+
.conflict-values pre { max-height: 20rem; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; font-size: 0.92rem; }
|
|
6728
|
+
.review-warnings .proposal-warnings { border: 0; padding: 0; background: transparent; }
|
|
6729
|
+
.no-net-change { padding: 1rem; border: 1px solid var(--line); border-radius: 8px; }
|
|
6730
|
+
.spinner { display: inline-block; width: 1em; height: 1em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: recheck-spin .7s linear infinite; }
|
|
6731
|
+
@keyframes recheck-spin { to { transform: rotate(360deg); } }
|
|
6732
|
+
@media (max-width: 850px) { .conflict-values dl { grid-template-columns: 1fr; } }
|
|
6733
|
+
@media (prefers-reduced-motion: reduce) { .spinner { animation: none; } }
|
|
6512
6734
|
.form-row { margin: 0 0 0.9rem; }
|
|
6513
6735
|
.form-row:last-child { margin-bottom: 0; }
|
|
6514
6736
|
.form-row > label, .form-row > .label { display: block; font-weight: 600; margin: 0 0 0.3rem; }
|
|
@@ -6556,26 +6778,19 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6556
6778
|
.conflict-list { margin: 0.4rem 0 0 1.2rem; padding: 0; }
|
|
6557
6779
|
.conflict-list li { margin: 0.25rem 0; }
|
|
6558
6780
|
@media (max-width: 640px) {
|
|
6559
|
-
nav.tabs { overflow-x: auto; flex-wrap: nowrap; }
|
|
6560
|
-
nav.tabs button { flex: 0 0 auto; }
|
|
6561
6781
|
.field-line { flex-wrap: wrap; }
|
|
6562
6782
|
}
|
|
6563
|
-
`}a(
|
|
6783
|
+
`}a(yy,"pageStylesDashboard");function Lc(){return(dy()+ly()+cy()+uy()+py()+fy()+hy()+my()+gy()+yy()).trim()}a(Lc,"pageStyles");function wy(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""")}a(wy,"escapeHtml");function by(){return`
|
|
6564
6784
|
<header class="page-header">
|
|
6565
6785
|
<h1>Node-RED AI Access</h1>
|
|
6566
6786
|
<p class="lede">
|
|
6567
6787
|
Let an AI assistant suggest changes to your flows \u2014 nothing changes until you approve.
|
|
6568
6788
|
</p>
|
|
6569
|
-
<ol class="how-it-works" id="how-it-works" aria-label="How it works">
|
|
6570
|
-
<li><strong>Create a link</strong> \u2014 temporary access for your AI assistant.</li>
|
|
6571
|
-
<li><strong>Give it to your AI</strong> \u2014 paste the link in the chat.</li>
|
|
6572
|
-
<li><strong>Review proposals</strong> \u2014 approve to deploy, or reject.</li>
|
|
6573
|
-
</ol>
|
|
6574
6789
|
</header>
|
|
6575
6790
|
<div id="boot-flash" class="flash" hidden></div>
|
|
6576
6791
|
<div id="auth-gate" class="auth-gate" hidden>
|
|
6577
6792
|
<div class="sg-card" role="dialog" aria-modal="true" aria-labelledby="auth-gate-title">
|
|
6578
|
-
<div class="sg-head">${
|
|
6793
|
+
<div class="sg-head">${Xi}<div class="sg-brand"><b>Node-RED AI Access</b><span>Dashboard</span></div></div>
|
|
6579
6794
|
<div class="sg-body">
|
|
6580
6795
|
<h1 id="auth-gate-title">Sign in to Node-RED</h1>
|
|
6581
6796
|
<p class="sg-sub">AI Access uses your Node-RED editor account. Signing in here also signs in the editor in this browser.</p>
|
|
@@ -6595,20 +6810,21 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6595
6810
|
</div>
|
|
6596
6811
|
</div>
|
|
6597
6812
|
<div id="toast-host" class="toast-host" aria-live="polite"></div>
|
|
6598
|
-
<
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
<
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
</button
|
|
6606
|
-
|
|
6607
|
-
|
|
6813
|
+
<div class="settings-layout">
|
|
6814
|
+
${vy()}`}a(by,"renderMcpPageHeader");function vy(){return`<nav class="tabs" aria-label="Admin views" hidden id="main-nav">
|
|
6815
|
+
${[{title:"Overview",items:[["status","Overview","M8 1 1 7v8h5v-5h4v5h5V7L8 1Zm0 2 5.5 4.8V13H12V8H4v5H2.5V7.8Z"]]},{title:"Access",items:[["links","Access tokens","M10.5 1a4.5 4.5 0 0 0-4.3 5.8L1 12v3h4v-2h2v-2h2l.8-.8A4.5 4.5 0 1 0 10.5 1Zm0 1.5a3 3 0 1 1 0 6 3 3 0 0 1 0-6ZM2.5 12.6l4.5-4.5 1 1-1.4 1.4H5.5v1H4v2H2.5Z"],["oauth","OAuth","M8 0 1 3v5c0 4 7 8 7 8s7-4 7-8V3L8 0Zm0 1.6 5.5 2.4v4c0 2.5-3.8 5.3-5.5 6.3C6.3 13.3 2.5 10.5 2.5 8V4Z"]]},{title:"Proposals",items:[["proposals","Proposals","M3 1a2 2 0 1 0 0 4 2 2 0 0 0 0-4ZM2.25 6v5a2 2 0 1 0 1.5 0V6ZM11 1v3H7v1.5h4V9h1.5V5.5H16V4h-3.5V1Z"]]},{title:"Advanced",items:[["settings","Settings","M0 3h3V1h1.5v2H16v1.5H4.5V7H3V4.5H0Zm0 8h10V9h1.5v2H16v1.5h-4.5V15H10v-2.5H0Z"],["logs","Audit log","M3 1h10v14H3V1Zm1.5 1.5v11h7v-11ZM6 4h4v1.5H6Zm0 3h4v1.5H6Zm0 3h4v1.5H6Z"]]}].map(t=>`<div class="nav-group"${t.items.length===1&&t.items[0]?.[1]===t.title?"":` role="group" aria-labelledby="nav-group-${t.title.toLowerCase()}"`}>
|
|
6816
|
+
<div class="nav-group-title" id="nav-group-${t.title.toLowerCase()}"${t.items.length===1&&t.items[0]?.[1]===t.title?' aria-hidden="true"':""}>${t.title}</div>
|
|
6817
|
+
${t.items.map(([n,r,o])=>`<button type="button" data-tab="${n}" aria-controls="section-${n}" aria-current="${n==="status"?"page":"false"}">
|
|
6818
|
+
<svg aria-hidden="true" focusable="false" width="16" height="16" viewBox="0 0 16 16" fill="currentColor"><path d="${o}"/></svg>
|
|
6819
|
+
<span>${r}</span>${n==="proposals"?' <span class="tab-badge" id="proposals-pending-badge" hidden>0</span>':""}
|
|
6820
|
+
</button>`).join("")}
|
|
6821
|
+
</div>`).join("")}
|
|
6822
|
+
</nav>`}a(vy,"renderMcpPageNav");function ky(){return`
|
|
6608
6823
|
<section class="panel active" id="section-status" aria-label="Overview" hidden>
|
|
6609
|
-
<h2>Overview</h2>
|
|
6824
|
+
<h2 tabindex="-1">Overview</h2>
|
|
6610
6825
|
<div id="status-flash" class="flash" hidden></div>
|
|
6611
6826
|
<aside id="status-warning" class="warning" role="status" hidden></aside>
|
|
6827
|
+
<h3>Activity</h3>
|
|
6612
6828
|
<div class="dash-grid" id="status-dashboard">
|
|
6613
6829
|
<div class="dash-card">
|
|
6614
6830
|
<div class="dash-label">AI access</div>
|
|
@@ -6648,20 +6864,20 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6648
6864
|
<div class="row" style="margin-top:1rem">
|
|
6649
6865
|
<button type="button" class="action secondary" id="refresh-status">Refresh</button>
|
|
6650
6866
|
</div>
|
|
6651
|
-
</section>`}a(
|
|
6867
|
+
</section>`}a(ky,"renderMcpPageStatusSection");function Ay(){return`
|
|
6652
6868
|
<section class="panel" id="section-links" aria-label="Access tokens" hidden>
|
|
6653
|
-
<h2>Access tokens</h2>
|
|
6869
|
+
<h2 tabindex="-1">Access tokens</h2>
|
|
6654
6870
|
<p class="muted" id="links-intro">
|
|
6655
6871
|
Create a token for the channel your AI uses: a GET link to paste in chat,
|
|
6656
6872
|
a REST Bearer, or an MCP token. It can read flows and submit proposals \u2014
|
|
6657
6873
|
it cannot deploy anything until you approve.
|
|
6658
6874
|
</p>
|
|
6659
6875
|
<div id="links-flash" class="flash" hidden></div>
|
|
6660
|
-
<div id="links-manage"></div>
|
|
6661
|
-
<div id="links-list"></div>
|
|
6662
|
-
</section>`}a(
|
|
6876
|
+
<div class="settings-subpart"><h3>Create access token</h3><div id="links-manage"></div></div>
|
|
6877
|
+
<h3>Access tokens</h3><div id="links-list"></div>
|
|
6878
|
+
</section>`}a(Ay,"renderMcpPageLinksSection");function xe(e){return`<span class="lock-badge" id="lock-${e}" hidden>Locked by settings.js</span>`}a(xe,"lockBadge");function xy(){return`
|
|
6663
6879
|
<section class="panel" id="section-settings" aria-label="Settings" hidden>
|
|
6664
|
-
<h2>Settings</h2>
|
|
6880
|
+
<h2 tabindex="-1">Settings</h2>
|
|
6665
6881
|
<p class="muted">Changes save automatically and apply immediately (no restart).</p>
|
|
6666
6882
|
<div id="settings-flash" class="flash" hidden></div>
|
|
6667
6883
|
<p id="settings-readonly" class="notice" hidden>Read-only: full admin permission (*) is required to change these settings.</p>
|
|
@@ -6672,13 +6888,13 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6672
6888
|
<label class="switch-label"><input type="checkbox" id="set-enabled" />
|
|
6673
6889
|
<span><strong>Enable AI Access features</strong>
|
|
6674
6890
|
<span class="hint">When off, access tokens, proposals, and AI tools are disabled.</span></span></label>
|
|
6675
|
-
${
|
|
6891
|
+
${xe("enabled")}
|
|
6676
6892
|
</div>
|
|
6677
6893
|
<div class="form-row toggle-row" id="row-allow-without" hidden>
|
|
6678
6894
|
<label class="switch-label"><input type="checkbox" id="set-allow-without" />
|
|
6679
6895
|
<span><strong>Allow AI Access without adminAuth (local-only)</strong>
|
|
6680
6896
|
<span class="hint danger-text">Not for production. Anyone who can reach the editor admin port can use AI features.</span></span></label>
|
|
6681
|
-
${
|
|
6897
|
+
${xe("allow-without")}
|
|
6682
6898
|
</div>
|
|
6683
6899
|
<div class="form-row">
|
|
6684
6900
|
<label for="set-deploy">Default deploy type</label>
|
|
@@ -6688,7 +6904,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6688
6904
|
<option value="flows">Changed flows</option>
|
|
6689
6905
|
<option value="full">Full redeploy</option>
|
|
6690
6906
|
</select>
|
|
6691
|
-
${
|
|
6907
|
+
${xe("deploy")}
|
|
6692
6908
|
</div>
|
|
6693
6909
|
<p class="hint danger-text" id="set-deploy-full-warn" hidden>Full redeploy restarts the entire runtime.</p>
|
|
6694
6910
|
</div>
|
|
@@ -6696,15 +6912,15 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6696
6912
|
<label for="set-ttl">Default access token expiry</label>
|
|
6697
6913
|
<div class="field-line">
|
|
6698
6914
|
<input type="number" id="set-ttl" min="1" step="1" class="num" />
|
|
6699
|
-
<span class="unit">hours</span>
|
|
6700
|
-
${
|
|
6915
|
+
<span class="unit">hours (minimum 1)</span>
|
|
6916
|
+
${xe("ttl")}
|
|
6701
6917
|
</div>
|
|
6702
6918
|
</div>
|
|
6703
6919
|
<div class="form-row">
|
|
6704
6920
|
<label for="set-proposal-ttl">Proposal expiry (days)</label>
|
|
6705
6921
|
<div class="field-line">
|
|
6706
6922
|
<input type="number" id="set-proposal-ttl" min="1" max="365" step="1" value="7" class="num" />
|
|
6707
|
-
${
|
|
6923
|
+
${xe("proposal-ttl")}
|
|
6708
6924
|
</div>
|
|
6709
6925
|
<p class="hint">Open proposals expire after this many days from submission (1\u2013365, default 7).</p>
|
|
6710
6926
|
</div>
|
|
@@ -6713,7 +6929,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6713
6929
|
<div class="field-line">
|
|
6714
6930
|
<input type="number" id="set-max-links" min="1" step="1" class="num" />
|
|
6715
6931
|
<span class="unit">tokens</span>
|
|
6716
|
-
${
|
|
6932
|
+
${xe("max-links")}
|
|
6717
6933
|
</div>
|
|
6718
6934
|
</div>
|
|
6719
6935
|
</div>
|
|
@@ -6726,7 +6942,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6726
6942
|
<div class="field-line">
|
|
6727
6943
|
<input type="url" id="set-public-url" placeholder="https://nodered.example.com" class="grow" />
|
|
6728
6944
|
<button type="button" class="action secondary" id="set-check-public-url">Check</button>
|
|
6729
|
-
${
|
|
6945
|
+
${xe("public-url")}
|
|
6730
6946
|
</div>
|
|
6731
6947
|
<div id="set-public-url-warning" class="hint warn-text" role="status" hidden></div>
|
|
6732
6948
|
<div id="set-check-result" class="check-result" aria-live="polite" hidden></div>
|
|
@@ -6744,7 +6960,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6744
6960
|
<label for="set-cors">Allowed origins <span class="hint">(comma separated)</span></label>
|
|
6745
6961
|
<div class="field-line">
|
|
6746
6962
|
<input type="text" id="set-cors" class="grow" placeholder="https://app.example.com" />
|
|
6747
|
-
${
|
|
6963
|
+
${xe("cors")}
|
|
6748
6964
|
</div>
|
|
6749
6965
|
</div>
|
|
6750
6966
|
</div>
|
|
@@ -6759,7 +6975,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6759
6975
|
<input type="text" id="set-secret-new" class="grow" maxlength="40" placeholder="e.g. PIN or SIGNING_KEY" aria-label="New secret name pattern" data-no-autosave="1" />
|
|
6760
6976
|
<button type="button" class="action secondary" id="set-secret-add">Add</button>
|
|
6761
6977
|
<button type="button" class="action secondary" id="set-secret-reset">Reset to defaults</button>
|
|
6762
|
-
${
|
|
6978
|
+
${xe("secret-patterns")}
|
|
6763
6979
|
</div>
|
|
6764
6980
|
<p class="hint">Letters, digits, <code>_</code> or <code>-</code> (2\u201340 characters). An empty list turns off name-based redaction; token formats are still redacted.</p>
|
|
6765
6981
|
</div>
|
|
@@ -6772,7 +6988,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6772
6988
|
<div class="field-line">
|
|
6773
6989
|
<input type="number" id="set-rate-window" min="1" step="1" class="num" />
|
|
6774
6990
|
<span class="unit">ms</span>
|
|
6775
|
-
${
|
|
6991
|
+
${xe("rate-window")}
|
|
6776
6992
|
</div>
|
|
6777
6993
|
</div>
|
|
6778
6994
|
<div class="form-row">
|
|
@@ -6780,7 +6996,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6780
6996
|
<div class="field-line">
|
|
6781
6997
|
<input type="number" id="set-rate-ip" min="1" step="1" class="num" />
|
|
6782
6998
|
<span class="unit">requests / window</span>
|
|
6783
|
-
${
|
|
6999
|
+
${xe("rate-ip")}
|
|
6784
7000
|
</div>
|
|
6785
7001
|
</div>
|
|
6786
7002
|
<div class="form-row">
|
|
@@ -6788,15 +7004,15 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6788
7004
|
<div class="field-line">
|
|
6789
7005
|
<input type="number" id="set-rate-token" min="1" step="1" class="num" />
|
|
6790
7006
|
<span class="unit">requests / window</span>
|
|
6791
|
-
${
|
|
7007
|
+
${xe("rate-token")}
|
|
6792
7008
|
</div>
|
|
6793
7009
|
</div>
|
|
6794
7010
|
<p class="hint" id="set-trust-proxy-inline"></p>
|
|
6795
7011
|
</div>
|
|
6796
7012
|
</div>
|
|
6797
|
-
</section>`}a(
|
|
7013
|
+
</section>`}a(xy,"renderMcpPageSettingsSection");function Sy(){return`
|
|
6798
7014
|
<section class="panel" id="section-oauth" aria-label="OAuth" hidden>
|
|
6799
|
-
<h2>MCP OAuth</h2>
|
|
7015
|
+
<h2 tabindex="-1">MCP OAuth</h2>
|
|
6800
7016
|
<p class="muted">MCP clients (ChatGPT, Claude, \u2026) can request read or propose access through OAuth.</p>
|
|
6801
7017
|
<div id="oauth-flash" class="flash" hidden></div>
|
|
6802
7018
|
<div id="oauth-route-warning" class="warning" role="status" hidden></div>
|
|
@@ -6807,7 +7023,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6807
7023
|
<label class="switch-label"><input type="checkbox" id="oauth-enabled" />
|
|
6808
7024
|
<span><strong>Enable MCP OAuth</strong>
|
|
6809
7025
|
<span class="hint">Also available in the editor sidebar (AI Access tab).</span></span></label>
|
|
6810
|
-
${
|
|
7026
|
+
${xe("oauth")}
|
|
6811
7027
|
</div>
|
|
6812
7028
|
<div class="form-row">
|
|
6813
7029
|
<span class="label">Server URL for MCP clients</span>
|
|
@@ -6869,9 +7085,9 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6869
7085
|
<button type="button" class="action secondary" id="oauth-refresh">Refresh</button></div>
|
|
6870
7086
|
<div id="oauth-clients" aria-live="polite"><p class="muted">Loading\u2026</p></div>
|
|
6871
7087
|
</div>
|
|
6872
|
-
</section>`}a(
|
|
7088
|
+
</section>`}a(Sy,"renderMcpPageOAuthSection");function Ry(){return`
|
|
6873
7089
|
<section class="panel" id="section-proposals" aria-label="Proposals" hidden>
|
|
6874
|
-
<h2>Proposals</h2>
|
|
7090
|
+
<h2 tabindex="-1">Proposals</h2>
|
|
6875
7091
|
<p id="proposals-hint" class="muted"></p>
|
|
6876
7092
|
<div class="filter-row" id="proposals-filters" hidden>
|
|
6877
7093
|
<button type="button" class="filter-chip" data-filter="pending" aria-pressed="true">
|
|
@@ -6882,14 +7098,14 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6882
7098
|
</button>
|
|
6883
7099
|
<input type="search" id="proposals-search" class="proposals-search"
|
|
6884
7100
|
placeholder="Search title or id (e.g. efd159aa)\u2026"
|
|
6885
|
-
aria-label="Search proposals by title or
|
|
7101
|
+
aria-label="Search proposals by title or id"
|
|
6886
7102
|
autocomplete="off" />
|
|
6887
7103
|
</div>
|
|
6888
7104
|
<div id="proposals-flash" class="flash" hidden></div>
|
|
6889
7105
|
<div id="proposals-list"><p class="muted">Loading\u2026</p></div>
|
|
6890
|
-
</section>`}a(
|
|
7106
|
+
</section>`}a(Ry,"renderMcpPageProposalsSection");function Ty(){return`
|
|
6891
7107
|
<section class="panel" id="section-logs" aria-label="Audit log" hidden>
|
|
6892
|
-
<h2>Audit log</h2>
|
|
7108
|
+
<h2 tabindex="-1">Audit log</h2>
|
|
6893
7109
|
<div id="logs-flash" class="flash" hidden></div>
|
|
6894
7110
|
<div class="row logs-toolbar">
|
|
6895
7111
|
<label>Show
|
|
@@ -6903,7 +7119,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6903
7119
|
</label>
|
|
6904
7120
|
</div>
|
|
6905
7121
|
<div id="logs-list"><p class="muted">Loading\u2026</p></div>
|
|
6906
|
-
</section>`}a(
|
|
7122
|
+
</section>`}a(Ty,"renderMcpPageLogsSection");function Py(){return`
|
|
6907
7123
|
<dialog id="reject-dialog">
|
|
6908
7124
|
<form method="dialog" id="reject-form">
|
|
6909
7125
|
<h3>Reject proposal</h3>
|
|
@@ -6945,7 +7161,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6945
7161
|
<button type="submit" value="confirm" class="action danger" id="confirm-action-btn">Confirm</button>
|
|
6946
7162
|
</div>
|
|
6947
7163
|
</form>
|
|
6948
|
-
</dialog>`}a(
|
|
7164
|
+
</dialog>`}a(Py,"renderMcpPageDialogs");function Nc(e){let t={apiBase:e.apiBase,mcpRpc:e.mcpRpc??"/mcp/rpc",dashboardPath:e.dashboardPath??"/ai-access",editorRoot:e.editorRoot,authTokenKey:e.authTokenKey};return`<!DOCTYPE html>
|
|
6949
7165
|
<html lang="en">
|
|
6950
7166
|
<head>
|
|
6951
7167
|
<meta charset="utf-8" />
|
|
@@ -6953,23 +7169,24 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
6953
7169
|
<meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" />
|
|
6954
7170
|
<meta http-equiv="Pragma" content="no-cache" />
|
|
6955
7171
|
<title>Node-RED AI Access</title>
|
|
6956
|
-
<style>${
|
|
7172
|
+
<style>${Lc()}</style>
|
|
6957
7173
|
</head>
|
|
6958
7174
|
<body>
|
|
6959
7175
|
<main
|
|
6960
7176
|
id="mcp-root"
|
|
6961
|
-
data-config="${
|
|
6962
|
-
>${
|
|
6963
|
-
${
|
|
6964
|
-
${
|
|
6965
|
-
${
|
|
6966
|
-
${
|
|
6967
|
-
${
|
|
6968
|
-
${
|
|
6969
|
-
|
|
7177
|
+
data-config="${wy(JSON.stringify(t))}"
|
|
7178
|
+
>${by()}
|
|
7179
|
+
${ky()}
|
|
7180
|
+
${xy()}
|
|
7181
|
+
${Ay()}
|
|
7182
|
+
${Sy()}
|
|
7183
|
+
${Ry()}
|
|
7184
|
+
${Ty()}
|
|
7185
|
+
</div>
|
|
7186
|
+
${Py()}
|
|
6970
7187
|
</main>
|
|
6971
|
-
<script>${
|
|
7188
|
+
<script>${Ic()}</script>
|
|
6972
7189
|
</body>
|
|
6973
|
-
</html>`}a(hc,"renderMcpPage");Fe();et();Te();Je();se();$t();ie();tt();zt();Fe();Io();et();Pe();function Ue(e,t,n,r={}){e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("Content-Type","application/json; charset=utf-8");let o={error:nt[n]??"Request failed",code:n,...r},s=we(n,"rest");s&&typeof o.hint!="string"&&(o.hint=s),e.status(t).json(o)}a(Ue,"sendSafeJsonError");function zi(e){let t={kind:"admin"};return typeof e.user?.username=="string"&&(t.username=e.user.username),t}a(zi,"adminActor");function mc(e,t,n,r,o){return async(s,i)=>{if(!s.accessLink){i.setHeader("WWW-Authenticate",ce),i.status(401).json({error:xe,code:qe});return}try{let d=V(s,r),l=ge(s.query),c=await Tt(e,t,s.accessLink,s.body??{},n,d,o),u=ne(e),p=Ve(e,e.get(c.id),s.accessLink.id),f=c.warnings;i.status(201).json({...fe(c,{...d,include:l}),mine:!0,...Ye(p),...ue(u),...f&&f.length>0?{warnings:f}:{}})}catch(d){let l=W(d,"rest");if(l){i.status(l.statusCode).json(l.body);return}B(i,d);return}}}a(mc,"createProposalHandler");function gc(e,t,n){return async(r,o)=>{let s=await Qe(t),i=s?.rev,d=s?co(s.flows):void 0,l={...V(r,n),...d?{liveFlowLabels:d}:{}},c=ge(r.query),u;try{u=xo(r.query)}catch(x){B(o,x);return}let p=s?$e(e,s).expired:ne(e),f=r.accessLink;if(f){let x=Tn(e,f,l,u),$=x.proposals.map(L=>{let D=he(e,f,L,{...l,include:c},{includeConflicts:!1});return!U(D.status)||i===void 0?D:{...D,baseRevStale:D.expectedRev!==i}});o.json({proposals:$,...Jt({...x,returned:x.proposals.length}),...ue(p)});return}let h={};u.flowId&&(h.flowId=u.flowId);let m=Da(e.list(h).filter(x=>di(x.status,u.status)),{...l,include:c}),g=u.offset??0,A=(u.limit!==void 0?m.slice(g,g+u.limit):g>0?m.slice(g):m).map(x=>!U(x.status)||i===void 0?x:{...x,baseRevStale:x.expectedRev!==i});o.json({proposals:A,...Jt({total:m.length,limit:u.limit??null,offset:g,returned:A.length}),...ue(p)})}}a(gc,"listProposalsHandler");function yc(e,t,n){return async(r,o)=>{let s=r.params.id;if(typeof s!="string"||s.length===0){o.status(400).json({error:"Missing proposal id"});return}let i=V(r,t),d=ge(r.query),l=await Qe(n);try{if(r.accessLink){let c=ct(e,r.accessLink,s,i);o.json(he(e,r.accessLink,c,{...i,include:d},{...l?{live:l}:{}}));return}l?$e(e,l):ne(e),o.json(fe(e.get(s),{...i,include:d}))}catch(c){if(c instanceof me){Ue(o,404,c.code);return}let u=W(c,"rest");if(u){o.status(u.statusCode).json(u.body);return}B(o,c);return}}}a(yc,"getProposalHandler");function wc(e,t){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){r.status(400).json({error:"Missing proposal id"});return}let s=n.body??{},i={};if(s.mergedHash!==void 0){if(typeof s.mergedHash!="string"||!/^[0-9a-f]{8,64}$/.test(s.mergedHash)){r.status(400).json({error:"mergedHash must be a hex string",code:"invalid_body"});return}i.mergedHash=s.mergedHash}if(s.deploymentType!==void 0){if(s.deploymentType!=="nodes"&&s.deploymentType!=="flows"&&s.deploymentType!=="full"){r.status(400).json({error:"deploymentType must be 'nodes', 'flows', or 'full'"});return}i.deploymentType=s.deploymentType}try{let d=await e.approve(o,zi(n),i);r.json(pe(d,V(n,t)))}catch(d){if(d instanceof Ke){Ue(r,409,d.code,{conflicts:d.conflicts});return}if(d instanceof Yt){Ue(r,409,d.code,{mergedHash:d.mergedHash,rebaseState:d.rebaseState});return}if(d instanceof Xt||d instanceof Pt||d instanceof Zt){Ue(r,d.statusCode,d.code);return}if(d instanceof me||d instanceof Y||d instanceof Re||d instanceof le){Ue(r,d.statusCode,d.code);return}B(r,d);return}}}a(wc,"approveProposalHandler");function bc(e,t){return(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){r.status(400).json({error:"Missing proposal id"});return}let s=n.body??{},i=typeof s.reason=="string"?s.reason:"Rejected by admin";try{let d=pe(e.reject(o,zi(n),i),V(n,t));r.json(d)}catch(d){if(d instanceof me||d instanceof Y||d instanceof le){Ue(r,d.statusCode,d.code);return}B(r,d);return}}}a(bc,"rejectProposalHandler");function vc(e,t,n){return(r,o)=>{if(!r.accessLink){o.setHeader("WWW-Authenticate",ce),o.status(401).json({error:xe,code:qe});return}let s=r.params.id;if(typeof s!="string"||s.length===0){o.status(400).json({error:"Missing proposal id",code:"missing_proposal_id"});return}let i=r.body??{},d=V(r,n),l=ge(r.query);try{let c=Rn(e,t,r.accessLink,s,i.reason,d);o.json(he(e,r.accessLink,c,{...d,include:l}))}catch(c){let u=W(c,"rest");if(u){Ue(o,u.statusCode,u.body.code);return}Ue(o,500,"internal_error");return}}}a(vc,"withdrawProposalHandler");function kc(e,t){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){r.status(400).json({error:"Missing proposal id"});return}try{let s=n.accessLink?ct(e,n.accessLink,o):e.get(o),i=In(n.query.format);if(!i.ok){r.status(400).json({error:"Invalid diff format",code:"invalid_diff_format",hint:"Valid formats: summary, full (default summary)."});return}let d=Ln(await $n(s,Xe(n,t),e.get(o)),i.format);r.json(d)}catch(s){if(s instanceof me){Ue(r,404,s.code);return}if(s instanceof X||s instanceof Le||s instanceof ze){Ue(r,s.statusCode,s.code);return}let i=W(s,"rest");if(i){r.status(i.statusCode).json(i.body);return}B(r,s);return}}}a(kc,"getProposalDiffHandler");function Ac(e,t,n,r,o){return async(s,i)=>{if(!s.accessLink){Ue(i,401,"access_link_missing");return}try{let d=V(s,r),l=await To(e,t,s.accessLink,String(s.params.id),s.body??{},n,d,o);i.json(he(e,s.accessLink,l,d))}catch(d){B(i,d)}}}a(Ac,"resolveProposalHandler");function Sc(e,t,n,r){return async(o,s)=>{try{let i=o.body??{},d=await Ld(e,t,zi(o),String(o.params.id),i.nodeId,i.choice,n);s.json(fe(d,V(o,r)))}catch(i){B(s,i)}}}a(Sc,"resolveProposalConflictHandler");function xc(e,t,n){return async(r,o)=>{try{ne(e);let s=e.get(String(r.params.id)),i=await Qe(t);i&&U(s.status)&&(s=Ze(e,s,i).proposal),o.json(fe(s,V(r,n)))}catch(s){B(o,s)}}}a(xc,"recheckProposalHandler");Pe();Te();Te();ro();function ly(e){return e.endsWith("/")?e.slice(0,-1):e}a(ly,"trimTrailingSlash");var as=class{static{a(this,"HttpAdapter")}kind="http";baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=ly(t.baseUrl),this.accessToken=t.accessToken,this.fetchImpl=t.fetch??globalThis.fetch.bind(globalThis)}headers(t){let n=new Headers(t);return n.set("Accept","application/json"),n.set("Node-RED-API-Version","v2"),this.accessToken&&n.set("Authorization",`Bearer ${this.accessToken}`),n}async request(t,n={}){return await this.fetchImpl(`${this.baseUrl}${t}`,{...n,headers:this.headers(n.headers)})}async readJson(t,n){let r=await t.text();if(!t.ok){let o=r;try{let s=JSON.parse(r);o=s.message??s.code??r}catch{}throw t.status===404?new X(n):t.status===409?new te("unknown","unknown",`Admin API ${n}`):t.status===401||t.status===403?new ze(Yr):new Error(`Admin API ${n} failed (${t.status}): ${o}`)}if(!r)return null;try{return JSON.parse(r)}catch{throw new Error(`Admin API ${n} returned non-JSON`)}}async getFlows(){let t=await this.request("/flows"),n=await this.readJson(t,"GET /flows");return or(n,"GET /flows")}async getFlow(t){let n=encodeURIComponent(t),[r,o]=await Promise.all([this.request("/flows"),this.request(`/flow/${n}`)]);if(o.status===404)throw new X(t);let s=await this.readJson(r,"GET /flows"),i=await this.readJson(o,`GET /flow/${t}`);return{rev:or(s,"GET /flows").rev,flow:yn(t,i)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new te(t.expectedRev,r.rev,"updateFlow");let o=no(r.flows,t.flowId,t.flow),s=await this.request("/flows",{method:"POST",headers:{"Content-Type":"application/json","Node-RED-Deployment-Type":n},body:JSON.stringify({flows:o,rev:t.expectedRev})});if(s.status===409){let d=r.rev;try{let l=await s.clone().json();typeof l.current_rev=="string"&&(d=l.current_rev)}catch{}throw new te(t.expectedRev,d,"updateFlow")}let i=await this.readJson(s,"POST /flows");if(!i||typeof i!="object"||typeof i.rev!="string")throw new Error("Admin API POST /flows did not return a rev");return{flowId:t.flowId,rev:i.rev}}};Te();ro();function cy(e){let t=e.runtime?.flows;if(!t?.getFlows||!t.getFlow||!t.setFlows)throw new Error("RED.runtime.flows is missing getFlows, getFlow, or setFlows");return t}a(cy,"requireFlowsApi");function uy(e){if(typeof e=="string")return e;if(typeof e.rev=="string"&&e.rev.length>0)return e.rev;throw new Error("RED.runtime.flows.setFlows did not return a rev")}a(uy,"resolveRev");var ds=class{static{a(this,"RedAdapter")}kind="red";flows;constructor(t){this.flows=cy(t)}async getFlows(){let t=await this.flows.getFlows({});return or(t,"RED.runtime.flows.getFlows()")}async getFlow(t){let n=await this.getFlows(),r;try{r=await this.flows.getFlow({id:t})}catch(o){throw py(o)?new X(t):o}return{rev:n.rev,flow:yn(t,r)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new te(t.expectedRev,r.rev,"updateFlow");let o=no(r.flows,t.flowId,t.flow),s;try{s=await this.flows.setFlows({flows:{flows:o,rev:t.expectedRev},deploymentType:n})}catch(i){if(i&&typeof i=="object"&&"code"in i&&i.code==="version_mismatch"){let d=await this.getFlows();throw new te(t.expectedRev,d.rev,"updateFlow")}throw i}return{flowId:t.flowId,rev:uy(s)}}};function Tc(e){let t=e?.runtime?.flows;return!!(t?.getFlows&&t.getFlow&&t.setFlows)}a(Tc,"canUseRedRuntime");function py(e){if(!e||typeof e!="object")return!1;let t=e;if((typeof t.status=="number"?t.status:t.statusCode)===404||t.code==="not_found"||t.code==="flow_not_found")return!0;let r=typeof t.message=="string"?t.message.toLowerCase():"";return r.includes("not found")||r.includes("unknown flow")}a(py,"isRuntimeFlowNotFound");var Rc=require("module");function Pc(e){if(!e||typeof e!="object")return!1;let t=e;return typeof t.getFlows=="function"&&typeof t.getFlow=="function"&&typeof t.setFlows=="function"}a(Pc,"isFlowsApi");function fy(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Rc.createRequire)(e)}a(fy,"createPluginRequire");function hy(e){try{let t=e("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}a(hy,"isNodeRedRuntimeInitialized");function my(){try{let e=fy();if(!hy(e))return null;let t=e("@node-red/runtime");return Pc(t.flows)?t.flows:null}catch{return null}}a(my,"tryRequireInitializedRuntimeFlows");function _c(e){if(e?.runtime&&Pc(e.runtime.flows))return e;let t=my();return t?{runtime:{flows:t}}:null}a(_c,"resolveRuntimeHost");function Ec(e={}){let t=_c(e.RED??null);if(t&&Tc(t))return new ds(t);let n=e.adminAuthEnabled===!0;if(e.http?.baseUrl){if(n&&!e.http.accessToken)throw new Le("In-process Node-RED flow runtime is unavailable and HTTP Admin API fallback has no credentials while adminAuth is enabled. Ensure the plugin runs inside Node-RED so @node-red/runtime.flows can be used.");return new as(e.http)}throw new Le("No usable Node-RED flow adapter: in-process flows API is unavailable and no HTTP Admin API baseUrl was provided")}a(Ec,"createFlowAdapter");Te();Rs();var cs=require("fs"),Cc=require("path");Zr();se();var gy=/^[a-z][a-z0-9._-]{0,63}$/;function yy(e){if(e.kind==="system")return{kind:"system"};if(e.kind==="access-link"){let n={kind:"access-link",linkId:String(e.linkId).replace(/\0/g,"").slice(0,200)};return typeof e.label=="string"&&(n.label=e.label.replace(/\0/g,"").slice(0,200)),n}let t={kind:"admin"};return typeof e.username=="string"&&(t.username=e.username.replace(/\0/g,"").slice(0,200)),t}a(yy,"sanitizeActor");var ls=class{constructor(t,n={}){this.paths=t;this.filePath=(0,Cc.join)(t.logs,"audit.jsonl"),this.now=n.now??(()=>Date.now()),this.maxAppendBytes=n.maxAppendBytes??Vi}static{a(this,"AuditLog")}filePath;now;maxAppendBytes;seq=0;append(t,n,r){let o=gy.test(t)?t:"unknown";this.seq+=1;let s={id:`log-${this.now()}-${this.seq}`,at:new Date(this.now()).toISOString(),action:o,actor:yy(n)};r!==void 0&&(s.details=ha(r));let i=ft(s,this.maxAppendBytes,{kind:"audit"});return Xi(this.paths.root,this.filePath,i),s}list(t=100){if(!(0,cs.existsSync)(this.filePath))return[];let n=(0,cs.readFileSync)(this.filePath,"utf8").split(`
|
|
6974
|
-
`).map(o=>o.trim()).filter(Boolean),r=[];for(let o of n)try{r.push(JSON.parse(o))}catch{}return r.slice(-Math.max(1,t)).reverse()}};Je();qr();ve();qt();Io();ke();function wy(e){let t=a(()=>Promise.reject(new ze(e)),"fail");return{kind:"http",getFlows:t,getFlow:t,updateFlow:t}}a(wy,"misconfiguredAdapter");function Ic(e){let t=e.settings,n=typeof t.uiPort=="number"?t.uiPort:1880,r=t.httpAdminRoot??"/",o=!r||r==="/"?"":r.replace(/\/$/,"");return`http://127.0.0.1:${n}${o}`}a(Ic,"localAdminBaseUrl");function us(e,t={}){let n=Mr(e.settings.userDir);Zi(n);let r=t.settingsStore??new fn(n,e.settings.aiAccess,u=>e.log?.warn?.(u));t.bindSettings!==!1&&Wr(r),Kr(r.getEffective().publicBaseUrl);let o=t.linkStore??rs(e);o.configureDefaults({defaultTtlSeconds:r.getEffective().defaultLinkTtlSeconds,maxActiveLinks:r.getEffective().maxActiveLinks});let s=new Qr(n),i=new ls(n),d;if(t.flowAdapter)d=t.flowAdapter;else try{d=Ec({RED:e,http:{baseUrl:Ic(e)},adminAuthEnabled:oe(e.settings)})}catch(u){let p=u instanceof Le?u.message:Yr;e.log?.warn?.(`AI Access flow adapter unavailable: ${p}`),d=wy(p)}d=wa(d);let l;if(t.nodeTypesAdapter)l=t.nodeTypesAdapter;else if(t.flowAdapter)l=Ls();else try{l=Pa({baseUrl:Ic(e)})}catch(u){let p=u instanceof Error?u.message:String(u);e.log?.warn?.(`AI Access node-types adapter unavailable: ${p}`),l=Ls([])}let c=new Co(s,i,d,()=>Ee(e.settings).defaultDeploymentType,e.settings.aiAccess);return{linkStore:o,proposalStore:s,auditLog:i,flowAdapter:d,nodeTypesAdapter:l,proposalService:c,settingsStore:r,...t.catalogueFetcher?{catalogueFetcher:t.catalogueFetcher}:{},...t.catalogueUrl?{catalogueUrl:t.catalogueUrl}:{}}}a(us,"createPluginServices");se();ve();Be();Dr();var Lc=zn(require("dns"),1),Nc=zn(require("http"),1),Fc=zn(require("https"),1),Nr=require("net");ve();function by(e,t){return e===10||e===172&&t>=16&&t<=31||e===192&&t===168}a(by,"isPrivateV4");function vy(e,t){return e===169&&t===254}a(vy,"isLinkLocalV4");function ky(e,t,n){return e===0||e===100&&t>=64&&t<=127||e>=224||e===192&&t===0&&(n===0||n===2)||e===198&&(t===18||t===19||t===51&&n===100)||e===203&&t===0&&n===113}a(ky,"isSpecialUseV4");function Ay(e){let[t=0,n=0,r=0]=e.split(".").map(Number);return t===127?"loopback":by(t,n)||vy(t,n)||ky(t,n,r)||e==="168.63.129.16"?"blocked":"public"}a(Ay,"classifyIpv4");function Sy(e){let t=e.split(":"),n=t.indexOf(""),r=[];for(let o of t){if(!o)continue;if(o.includes(".")){let i=o.split(".").map(Number);if(i.length!==4||i.some(d=>!Number.isInteger(d)||d<0||d>255))return;r.push((i[0]??0)<<8|(i[1]??0)),r.push((i[2]??0)<<8|(i[3]??0));continue}let s=Number.parseInt(o,16);if(!/^[\da-f]{1,4}$/i.test(o)||!Number.isInteger(s))return;r.push(s)}if(n>=0){let o=8-r.length;if(o<1)return;let s=t.slice(0,n).filter(Boolean).length;r.splice(s,0,...Array(o).fill(0))}else if(r.length!==8)return;return r.length===8?r:void 0}a(Sy,"expandIpv6");function xy(e){let t=Sy(e);if(!t)return"blocked";if(t.slice(0,7).every(s=>s===0)&&t[7]===1)return"loopback";let n=t[0]??0,r=t[1]??0;return n===0||n<8192||n>16383||n===8193&&r<=511||n===8193&&r===3512||n===16383&&r<=4095||n===8194?"blocked":"public"}a(xy,"classifyIpv6");function Fr(e){let t=Mt(e);return t?(0,Nr.isIP)(t)===4?Ay(t):xy(t):"blocked"}a(Fr,"classifyPublicUrlAddress");function Ty(e){let t=e.toLowerCase().replace(/\.$/,"");return["metadata","metadata.google.internal","metadata.google.com","metadata.goog","instance-data","instance-data.ec2.internal","metadata.azure.internal","metadata.aws.internal"].some(n=>t===n||t.endsWith(`.${n}`))}a(Ty,"isMetadataHostname");async function Oc(e,t,n){let r=hn(e);if(!r)throw new Error("publicBaseUrl is required for Check");let o=new URL(r),s=o.hostname.replace(/^\[|\]$/g,"");if(Ty(s))throw new Error("Public URL Check cannot target metadata hosts");let i=Mt(s),l=(typeof e=="string"?/^https?:\/\/(\[[^\]]+\]|[^/?#:]+)/i.exec(e.trim())?.[1]:void 0)?.replace(/^\[|\]$/g,"");if(i&&l&&l.toLowerCase()!==i.toLowerCase())throw new Error("Public URL Check requires canonical IP address notation");let c=s.toLowerCase().replace(/\.$/,"")==="localhost"||i!==void 0&&Fr(i)==="loopback",u=Number(o.port||(o.protocol==="https:"?443:80));if(c&&u!==t)throw new Error("Public URL Check may only target this Node-RED instance's port for loopback URLs");let p=a(g=>{let y=Fr(g);if(y==="blocked"||y==="loopback"&&!c||c&&y!=="loopback")throw new Error("Public URL Check cannot target private or non-public addresses")},"validateAddress");if(i)return p(i),{origin:r,address:i,family:(0,Nr.isIP)(i)};let f=await new Promise((g,y)=>{let A=a(()=>y(n.reason instanceof Error?n.reason:new Error(String(n.reason))),"abort");n.throwIfAborted(),n.addEventListener("abort",A,{once:!0}),Lc.default.lookup(s,{all:!0},(x,$)=>{n.removeEventListener("abort",A),x?y(x):g($)})});if(n.throwIfAborted(),!f.length)throw new Error("Public URL Check hostname has no addresses");for(let g of f)p(g.address);let h=[];for(let g of f){let y=Mt(g.address);if(!y)throw new Error("Public URL Check returned an invalid address");h.some(A=>A.address===y)||h.push({address:y,family:(0,Nr.isIP)(y)})}c&&h.sort((g,y)=>g.family-y.family);let m=h[0];if(!m)throw new Error("Public URL Check hostname has no addresses");return{origin:r,...m,addresses:h}}a(Oc,"preparePublicUrlCheck");function Ry(e,t,n){let r=e.hostname.replace(/^\[|\]$/g,"");return new Promise((o,s)=>{let d=(e.protocol==="https:"?Fc.default:Nc.default).request(e,{method:"GET",agent:!1,signal:n,headers:{Host:e.host},...(0,Nr.isIP)(r)?{}:{servername:r},lookup:a((l,c,u)=>{c.all?u(null,[{address:t.address,family:t.family}]):u(null,t.address,t.family)},"lookup")},l=>{o(l.statusCode??0),l.destroy()});d.on("error",s),d.end()})}a(Ry,"requestPinned");async function $c(e,t,n){let r=new URL(e.origin+t),o=e.addresses?.length?e.addresses:[{address:e.address,family:e.family}],s;for(let i of o){n.throwIfAborted();try{return await Ry(r,i,n)}catch(d){if(n.aborted)throw d;s=d}}throw s instanceof Error?s:new Error("Public URL Check request failed")}a($c,"requestPublicUrlCheck");un();ke();function Py(e){return Zn(e.settings.httpAdminRoot)}a(Py,"mcpPageUrl");function _y(e){return typeof e=="boolean"||typeof e=="number"||typeof e=="string"?e:Array.isArray(e)?e.join(", "):typeof e=="function"?"custom function":!1}a(_y,"normalizeTrustProxy");function Hc(e){if(!e)return"Set a Public URL before enabling OAuth.";let t=new URL(e),n=Mt(t.hostname),r=t.hostname.toLowerCase().replace(/\.$/,"")==="localhost"||n==="::1"||n?.startsWith("127.");return t.protocol==="http:"&&!r?"OAuth Public URL uses HTTP; HTTPS is recommended.":null}a(Hc,"oauthPublicUrlWarning");function jc(e,t,n){let{sources:r,...o}=t.getEffective(),{permission:s}=Gr(e,n);return{ok:!0,adminAuthEnabled:oe(e.settings),canEdit:s==="*",permission:s,mcpPageUrl:Py(e),apiBase:Se(e.settings.httpAdminRoot),settings:o,oauthPublicUrlWarning:Hc(o.publicBaseUrl),sources:r,disk:t.getDisk(),trustProxy:_y(n.app.get("trust proxy")),precedence:["settings.js","disk","default"],defaults:{secretNamePatterns:[...Ft]}}}a(jc,"settingsResponse");function Uc(e,t){return(n,r)=>{r.json(jc(e,t,n))}}a(Uc,"getSettingsHandler");function Mc(e,t,n){let r=e[t];if(r!==void 0){if(typeof r!="boolean")throw new Error(`${t} must be a boolean`);n[t]=r}}a(Mc,"parseBooleanField");function Dc(e,t,n){let r=e[t];if(r!==void 0){if(typeof r!="number"||!Number.isFinite(r)||r<=0)throw new Error(`${t} must be a positive number`);n[t]=Math.floor(r)}}a(Dc,"parsePositiveIntField");function Ey(e,t,n){let r=e[t];if(r===void 0)return;let o=t==="rateLimitWindowMs"?100:1,s=t==="rateLimitWindowMs"?36e5:1e6;if(typeof r!="number"||!Number.isInteger(r)||r<o||r>s)throw new Error(`${t} must be an integer between ${o} and ${s}`);n[t]=r}a(Ey,"parseRateLimitField");function Cy(e,t){if(e.corsAllowedOrigins===void 0)return;if(!Array.isArray(e.corsAllowedOrigins))throw new Error("corsAllowedOrigins must be a list of HTTP(S) origins");let n=e.corsAllowedOrigins.map(r=>Kn(r));if(n.some(r=>r===void 0))throw new Error("corsAllowedOrigins must contain only HTTP(S) origins without paths, credentials, queries, or fragments");t.corsAllowedOrigins=[...new Set(n)]}a(Cy,"parseCorsField");function Iy(e,t,n){let r=e.secretNamePatterns;if(r!==void 0){if(r===null){n.push("secretNamePatterns");return}if(!Array.isArray(r)||r.some(o=>typeof o!="string"))throw new Error("secretNamePatterns must be a list of strings (or null to reset)");if(r.length>Jn)throw new Error(`secretNamePatterns allows at most ${Jn} entries`);for(let o of r)if(!Hr.test(o.trim()))throw new Error('secretNamePatterns entries must be 2-40 letters, digits, "_" or "-" (e.g. API_KEY)');t.secretNamePatterns=Vn(r)??[]}}a(Iy,"parseSecretPatternsField");function Bc(e,t,n){return(r,o)=>{let s=r.body;if(!s||typeof s!="object"||Array.isArray(s)){o.status(400).json({error:"Request body must be a settings object",code:"invalid_body"});return}let i={...s};for(let[c,u]of Object.entries(t.getEffective().sources))u==="settings.js"&&delete i[c];t.getEffective().sources.oauth==="settings.js"&&delete i.oauth;let d={},l=[];try{if(i.proposalTtlDays!==void 0){let c=i.proposalTtlDays;if(typeof c!="number"||!Number.isInteger(c)||c<1||c>365)throw new Error("proposalTtlDays must be an integer between 1 and 365");d.proposalTtlDays=c}if(i.oauth!==void 0){if(!i.oauth||typeof i.oauth!="object"||Array.isArray(i.oauth))throw new Error("oauth must be an object");let c=i.oauth,u={};if(c.enabled!==void 0){if(typeof c.enabled!="boolean")throw new Error("oauth.enabled must be a boolean");u.enabled=c.enabled}for(let f of["accessTokenTtlSec","refreshIdleTtlSec","dcrMaxClients"])if(c[f]!==void 0){if(typeof c[f]!="number"||!Number.isInteger(c[f])||c[f]<1||c[f]>31536e3)throw new Error(`oauth.${f} must be a positive integer`);u[f]=c[f]}let p=c.refreshMaxLifetimeSec;if(p!==void 0){if(typeof p!="number"||!Number.isInteger(p)||p<0||p>31536e4)throw new Error("oauth.refreshMaxLifetimeSec must be a non-negative integer");u.refreshMaxLifetimeSec=p}d.oauth=u}Mc(i,"enabled",d),Mc(i,"allowWithoutAdminAuth",d),Dc(i,"defaultLinkTtlSeconds",d),Dc(i,"maxActiveLinks",d)}catch(c){o.status(400).json({error:c.message,code:"invalid_body"});return}if(i.defaultDeploymentType!==void 0){let c=i.defaultDeploymentType;if(c!=="nodes"&&c!=="flows"&&c!=="full"){o.status(400).json({error:"defaultDeploymentType must be 'nodes', 'flows', or 'full'",code:"invalid_body"});return}d.defaultDeploymentType=c}try{i.publicBaseUrl!==void 0&&(d.publicBaseUrl=hn(i.publicBaseUrl)),Cy(i,d),Iy(i,d,l);for(let c of["rateLimitWindowMs","rateLimitMaxPerIp","rateLimitMaxPerToken"])Ey(i,c,d)}catch(c){o.status(400).json({error:c.message,code:"invalid_body"});return}try{let c=t.patch(d,l);Kr(c.publicBaseUrl),n.configureDefaults({defaultTtlSeconds:c.defaultLinkTtlSeconds,maxActiveLinks:c.maxActiveLinks});let u=jc(e,t,r),p=c.publicBaseUrl?new URL(c.publicBaseUrl):void 0,f=p?Mt(p.hostname.replace(/^\[|\]$/g,"")):void 0,h=f?Fr(f):void 0,m=[];h!==void 0&&h!=="public"&&m.push("Public URL uses a private or non-public IP address.");let g=Hc(c.publicBaseUrl);g&&c.publicBaseUrl&&m.push(g),o.json({...u,...m.length?{warnings:m}:{}})}catch(c){if(c instanceof le){o.status(c.statusCode).json({error:c.message,code:c.code});return}B(o,c);return}}}a(Bc,"putSettingsHandler");function qc(e){return async(t,n)=>{let r=new AbortController,o=setTimeout(()=>r.abort(),5e3),s;try{let i;try{let c=e.settings,p=e.server?.address(),f=t.body?.publicBaseUrl,h=typeof f=="string"?new URL(f.trim()).hostname:"";if((h.toLowerCase().replace(/^\[|\]$/g,"").replace(/\.$/,"")==="localhost"||Fr(h.replace(/^\[|\]$/g,""))==="loopback")&&typeof p=="string")throw new Error("Public URL Check does not support loopback URLs when Node-RED listens on a Unix socket");let g=p&&typeof p=="object"?p.port:c.uiPort??1880;i=await Oc(t.body?.publicBaseUrl,g,r.signal)}catch(c){if(r.signal.aborted)throw c;n.status(400).json({error:c.message,code:"invalid_body"});return}s=i.origin+Se(e.settings.httpAdminRoot);let d=await $c(i,Se(e.settings.httpAdminRoot),r.signal),l={status:d,ok:d>=200&&d<300};n.json({ok:l.ok,status:l.status,url:s,message:l.ok?"Discovery endpoint is reachable":`Discovery endpoint returned HTTP ${l.status}`})}catch{n.json({ok:!1,status:null,url:s,error:r.signal.aborted?"Public URL check timed out after 5 seconds":"Could not reach the discovery endpoint"})}finally{clearTimeout(o)}}}a(qc,"checkPublicUrlHandler");ke();$t();ke();ie();qr();var Ly="com55-ai-access",ps=new WeakMap;function Wc(e){let t=new Set;for(let n of String(e??"").split(",")){let[r,...o]=n.split(";").map(d=>d.trim().toLowerCase());if(!r)continue;let s=o.find(d=>d.startsWith("q=")),i=s?Number(s.slice(2)):1;Number.isFinite(i)&&i>0&&t.add(r)}return t}a(Wc,"acceptedMediaTypes");function Gc(e){let t=Wc(e.headers.accept);return t.has("text/event-stream")&&!t.has("text/html")}a(Gc,"isMcpStreamRequest");function Ny(e,t,n){e.httpAdmin.get("/mcp/api/v1",t,n,mi(e)),e.httpAdmin.get("/mcp/api/v1/",t,n,mi(e)),e.httpAdmin.get("/mcp/api/v1/openapi.yaml",t,n,lc()),e.httpAdmin.get("/mcp/api/v1/openapi.json",t,n,cc()),e.httpAdmin.get("/mcp/editor/ai-access-sidebar.js",t,(r,o)=>{o.setHeader("Cache-Control","no-store, no-cache, must-revalidate, max-age=0"),o.setHeader("Pragma","no-cache"),o.setHeader("Expires","0"),o.setHeader("X-AI-Access-Sidebar-Version",Cn),o.type("application/javascript").status(200).send(Lo())})}a(Ny,"registerMcpShellAndDiscovery");var Fy=/^\/mcp\/(?:api\/v1\/proposals|rpc$|a\/[^/]+\/(?:proposals|propose|draft))/;function Kc(e,t={}){let n=us(e,{...t,linkStore:t.linkStore??rs(e)});gd(()=>n.settingsStore.getEffective().proposalTtlDays),ps.get(e)?.();let r=wd(()=>wr(n.proposalStore,n.flowAdapter),500),o=a(()=>{r.cancel(),e.events?.removeListener?.("flows:started",r.trigger)},"cleanup");ps.set(e,o),e.events?.on("flows:started",r.trigger);let s=Qd(()=>n.settingsStore.getEffective()),{ip:i,token:d}=s,l=new ss(Mr(e.settings.userDir),()=>n.settingsStore.getEffective().oauth.dcrMaxClients),c=a(()=>n.settingsStore.getEffective().publicBaseUrl,"publicBase"),u=a(()=>`${c().replace(/\/$/,"")}/mcp`,"mcpUrl"),p=new is(l,u,()=>n.settingsStore.getEffective().oauth.accessTokenTtlSec,()=>n.settingsStore.getEffective().oauth.refreshIdleTtlSec,()=>n.settingsStore.getEffective().oauth.refreshMaxLifetimeSec),f=sc(e,p,c,u);ea(e.httpAdmin,Qi(()=>n.settingsStore.getEffective().corsAllowedOrigins));let h=a(v=>(Array.isArray(v)?v:v?[v]:[]).flatMap(S=>{if(typeof S=="string")return[S];let N=S;return N&&typeof N.path=="string"?[{path:N.path,root:typeof N.root=="string"?N.root:void 0}]:[]}),"staticMounts"),m={authorization:[],metadata:[],details:[],httpNodeRootFallback:e.settings.httpNodeRoot===!1||!e.httpNode},g="",y=a(()=>{let v=oc({httpAdmin:e.httpAdmin,httpNode:e.httpNode,httpAdminRoot:e.settings.httpAdminRoot,httpNodeRoot:e.settings.httpNodeRoot,metadataFallback:m.httpNodeRootFallback,eachNode:e.nodes?I=>e.nodes?.eachNode(I):void 0,staticDirs:h(e.settings.httpStatic),httpStaticRoot:e.settings.httpStaticRoot,fileExists:zc.existsSync}),S=v.authorization,N=v.metadata;m.authorization=S.map(ji),m.metadata=N.map(ji),m.details=[...S.map(I=>({...I,kind:"authorization"})),...N.map(I=>({...I,kind:"metadata"}))];let w=[...m.authorization.map(I=>`${I} \u2014 OAuth authorization routes disabled`),...m.metadata.map(I=>`${I} \u2014 OAuth metadata disabled`)],F=w.join(`
|
|
6975
|
-
`);if(
|
|
7190
|
+
</html>`}a(Nc,"renderMcpPage");qe();lt();y();Ee();tt();de();Gt();le();ct();en();qe();Bo();lt();Ne();function $e(e,t,n,r={}){e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("Content-Type","application/json; charset=utf-8");let o={error:ut[n]??"Request failed",code:n,...r},s=Ae(n,"rest");s&&typeof o.hint!="string"&&(o.hint=s),e.status(t).json(o)}a($e,"sendSafeJsonError");function tr(e){let t={kind:"admin"};return typeof e.user?.username=="string"&&(t.username=e.user.username),t}a(tr,"adminActor");function Fc(e,t,n,r,o){return async(s,i)=>{if(!s.accessLink){i.setHeader("WWW-Authenticate",ge),i.status(401).json({error:He,code:ht});return}try{let d=Z(s,r),l=ve(s.query),c=await Ot(e,t,s.accessLink,s.body??{},n,d,o),u=se(e),p=it(e,e.get(c.id),s.accessLink.id),f=c.warnings;i.status(201).json({...ue(c,{...d,include:l}),mine:!0,...at(p),...me(u),...f&&f.length>0?{warnings:f}:{}})}catch(d){let l=J(d,"rest");if(l){i.status(l.statusCode).json(l.body);return}z(i,d);return}}}a(Fc,"createProposalHandler");function Oc(e,t,n){return async(r,o)=>{let s=await dt(t),i=s?.rev,d=s?ko(s.flows):void 0,l={...Z(r,n),...d?{liveFlowLabels:d}:{}},c=ve(r.query),u;try{u=Oo(r.query)}catch(k){z(o,k);return}let p=s?ze(e,s).expired:se(e),f=r.accessLink;if(f){let k=Dn(e,f,l,u),$=k.proposals.map(C=>{let O=be(e,f,C,{...l,include:c},{includeConflicts:!1});return!U(O.status)||i===void 0?O:{...O,baseRevChanged:O.expectedRev!==i}});o.json({proposals:$,...rn({...k,returned:k.proposals.length}),...me(p)});return}let h={};u.flowId&&(h.flowId=u.flowId);let m=rd(e.list(h).filter(k=>Si(k.status,u.status)),{...l,include:c}),g=u.offset??0,v=(u.limit!==void 0?m.slice(g,g+u.limit):g>0?m.slice(g):m).map(k=>!U(k.status)||i===void 0?k:{...k,baseRevChanged:k.expectedRev!==i});o.json({proposals:v,deployUnknownBlocks:e.list().filter(k=>k.deployPending?.queueReleased).map(k=>({proposalId:k.id,attemptId:k.deployPending?.attemptId,flowIds:[k.payload.flowId]})),...rn({total:m.length,limit:u.limit??null,offset:g,returned:v.length}),...me(p)})}}a(Oc,"listProposalsHandler");function $c(e,t,n){return async(r,o)=>{let s=r.params.id;if(typeof s!="string"||s.length===0){o.status(400).json({error:"Missing proposal id"});return}let i=Z(r,t),d=ve(r.query),l=await dt(n);try{if(r.accessLink){let c=bt(e,r.accessLink,s,i);o.json(be(e,r.accessLink,c,{...i,include:d},{...l?{live:l}:{}}));return}l?ze(e,l):se(e),o.json(ue(e.get(s),{...i,include:d}))}catch(c){if(c instanceof ye){$e(o,404,c.code);return}let u=J(c,"rest");if(u){o.status(u.statusCode).json(u.body);return}z(o,c);return}}}a($c,"getProposalHandler");function Mc(e,t){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){r.status(400).json({error:"Missing proposal id"});return}let s=n.body??{},i={};if(s.overrideDeployOutcomeUnknown!==void 0){if(typeof s.overrideDeployOutcomeUnknown!="boolean"){r.status(400).json({error:"overrideDeployOutcomeUnknown must be boolean",code:"invalid_body"});return}i.overrideDeployOutcomeUnknown=s.overrideDeployOutcomeUnknown}if(s.mergedHash!==void 0){if(typeof s.mergedHash!="string"||!/^[0-9a-f]{8,64}$/.test(s.mergedHash)){r.status(400).json({error:"mergedHash must be a hex string",code:"invalid_body"});return}i.mergedHash=s.mergedHash}if(s.deploymentType!==void 0){if(s.deploymentType!=="nodes"&&s.deploymentType!=="flows"&&s.deploymentType!=="full"){r.status(400).json({error:"deploymentType must be 'nodes', 'flows', or 'full'"});return}i.deploymentType=s.deploymentType}try{let d=await e.approve(o,tr(n),i);r.json(ce(d,Z(n,t)))}catch(d){if(d instanceof et){$e(r,409,d.code,{conflicts:d.conflicts});return}if(d instanceof sn){$e(r,409,d.code,{mergedHash:d.mergedHash,rebaseState:d.rebaseState});return}if(d instanceof At||d instanceof an||d instanceof $t||d instanceof dn){$e(r,d.statusCode,d.code);return}if(d instanceof At||d instanceof ye||d instanceof Q||d instanceof Le||d instanceof he){$e(r,d.statusCode,d.code);return}z(r,d);return}}}a(Mc,"approveProposalHandler");function Dc(e,t){return(n,r)=>{let o=n.body??{};try{let s=e.confirmLiveChecked(String(n.params.id),tr(n),o.attemptId);r.json(ce(s,Z(n,t)))}catch(s){if(s instanceof At||s instanceof ye){$e(r,s.statusCode,s.code);return}z(r,s)}}}a(Dc,"confirmProposalLiveCheckedHandler");function Hc(e,t){return(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){r.status(400).json({error:"Missing proposal id"});return}let s=n.body??{},i=typeof s.reason=="string"?s.reason:"Rejected by admin";try{let d=ce(e.reject(o,tr(n),i),Z(n,t));r.json(d)}catch(d){if(d instanceof At||d instanceof ye||d instanceof Q||d instanceof he){$e(r,d.statusCode,d.code);return}z(r,d);return}}}a(Hc,"rejectProposalHandler");function jc(e,t,n){return(r,o)=>{let s=r.params.id;if(typeof s!="string"||s.length===0){o.status(400).json({error:"Missing proposal id",code:"missing_proposal_id"});return}let i=r.body??{},d=Z(r,n),l=ve(r.query);try{if(!r.accessLink){let u=Pi(e,t,tr(r),s,i.reason,d);o.json(ue(u,{...d,include:l}));return}let c=Hn(e,t,r.accessLink,s,i.reason,d);o.json(be(e,r.accessLink,c,{...d,include:l}))}catch(c){let u=J(c,"rest");if(u){$e(o,u.statusCode,u.body.code);return}$e(o,500,"internal_error");return}}}a(jc,"withdrawProposalHandler");function Uc(e,t){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){r.status(400).json({error:"Missing proposal id"});return}try{let s=n.accessLink?bt(e,n.accessLink,o):e.get(o),i=zn(n.query.format);if(!i.ok){r.status(400).json({error:"Invalid diff format",code:"invalid_diff_format",hint:"Valid formats: summary, full (default summary)."});return}let d=Gn(await Yn(s,rt(n,t),e.get(o)),i.format);r.json(d)}catch(s){if(s instanceof ye){$e(r,404,s.code);return}if(s instanceof te||s instanceof je||s instanceof Ze){$e(r,s.statusCode,s.code);return}let i=J(s,"rest");if(i){r.status(i.statusCode).json(i.body);return}z(r,s);return}}}a(Uc,"getProposalDiffHandler");function Bc(e,t,n,r,o){return async(s,i)=>{if(!s.accessLink){$e(i,401,"access_link_missing");return}try{let d=Z(s,r),l=await $o(e,t,s.accessLink,String(s.params.id),s.body??{},n,d,o);i.json(be(e,s.accessLink,l,d))}catch(d){z(i,d)}}}a(Bc,"resolveProposalHandler");function qc(e,t,n,r){return async(o,s)=>{try{let i=o.body??{};if(i.choice==="undo"){let l=e.get(String(o.params.id)),c=l.conflictChoice;if(!c?.undo||c.nodeId!==i.nodeId){s.status(409).json({code:"conflict_not_found",error:"This choice can no longer be undone. Reload the proposal."});return}let u=e.updateBody(l.id,c.undo),p=await n.getFlows(),f=We(e,u,p).proposal;t.append("proposal.conflict_resolved",tr(o),{proposalId:l.id,nodeId:c.nodeId,choice:"undo"}),s.json(ue(f,Z(o,r)));return}let d=await _i(e,t,tr(o),String(o.params.id),i.nodeId,i.choice,n);s.json(ue(d,Z(o,r)))}catch(i){z(s,i)}}}a(qc,"resolveProposalConflictHandler");function Wc(e,t,n){return async(r,o)=>{try{se(e);let s=e.get(String(r.params.id)),i=await dt(t);i&&U(s.status)&&(s=We(e,s,i).proposal),o.json(ue(s,Z(r,n)))}catch(s){z(o,s)}}}a(Wc,"recheckProposalHandler");Ne();y();y();Ee();y();Ee();mo();function _y(e){return e.endsWith("/")?e.slice(0,-1):e}a(_y,"trimTrailingSlash");var ws=class{static{a(this,"HttpAdapter")}kind="http";baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=_y(t.baseUrl),this.accessToken=t.accessToken,this.fetchImpl=t.fetch??globalThis.fetch.bind(globalThis)}headers(t){let n=new Headers(t);return n.set("Accept","application/json"),n.set("Node-RED-API-Version","v2"),this.accessToken&&n.set("Authorization",`Bearer ${this.accessToken}`),n}async request(t,n={}){return await this.fetchImpl(`${this.baseUrl}${t}`,{...n,headers:this.headers(n.headers)})}async readJson(t,n){let r=await t.text();if(!t.ok){let o=r;try{let s=JSON.parse(r);o=s.message??s.code??r}catch{}throw t.status===404?new te(n):t.status===409?new re("unknown","unknown",`Admin API ${n}`):t.status===401||t.status===403?new Ze(io):new Error(`Admin API ${n} failed (${t.status}): ${o}`)}if(!r)return null;try{return JSON.parse(r)}catch{throw new Error(`Admin API ${n} returned non-JSON`)}}async getFlows(){let t=await this.request("/flows"),n=await this.readJson(t,"GET /flows");return yr(n,"GET /flows")}async getFlow(t){let n=encodeURIComponent(t),[r,o]=await Promise.all([this.request("/flows"),this.request(`/flow/${n}`)]);if(o.status===404)throw new te(t);let s=await this.readJson(r,"GET /flows"),i=await this.readJson(o,`GET /flow/${t}`);return{rev:yr(s,"GET /flows").rev,flow:_n(t,i)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new re(t.expectedRev,r.rev,"updateFlow");let o=ho(r.flows,t.flowId,t.flow),s=await this.request("/flows",{method:"POST",headers:{"Content-Type":"application/json","Node-RED-Deployment-Type":n},body:JSON.stringify({flows:o,rev:t.expectedRev})});if(s.status===409){let d=r.rev;try{let l=await s.clone().json();typeof l.current_rev=="string"&&(d=l.current_rev)}catch{}throw new re(t.expectedRev,d,"updateFlow")}let i=await this.readJson(s,"POST /flows");if(!i||typeof i!="object"||typeof i.rev!="string")throw new Error("Admin API POST /flows did not return a rev");return{flowId:t.flowId,rev:i.rev}}};y();Ee();mo();function Cy(e){let t=e.runtime?.flows;if(!t?.getFlows||!t.getFlow||!t.setFlows)throw new Error("RED.runtime.flows is missing getFlows, getFlow, or setFlows");return t}a(Cy,"requireFlowsApi");function Ey(e){if(typeof e=="string")return e;if(typeof e.rev=="string"&&e.rev.length>0)return e.rev;throw new Error("RED.runtime.flows.setFlows did not return a rev")}a(Ey,"resolveRev");var bs=class{static{a(this,"RedAdapter")}kind="red";flows;constructor(t){this.flows=Cy(t)}async getFlows(){let t=await this.flows.getFlows({});return yr(t,"RED.runtime.flows.getFlows()")}async getFlow(t){let n=await this.getFlows(),r;try{r=await this.flows.getFlow({id:t})}catch(o){throw Iy(o)?new te(t):o}return{rev:n.rev,flow:_n(t,r)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new re(t.expectedRev,r.rev,"updateFlow");let o=ho(r.flows,t.flowId,t.flow),s;try{s=await this.flows.setFlows({flows:{flows:o,rev:t.expectedRev},deploymentType:n})}catch(i){if(i&&typeof i=="object"&&"code"in i&&i.code==="version_mismatch"){let d=await this.getFlows();throw new re(t.expectedRev,d.rev,"updateFlow")}throw i}return{flowId:t.flowId,rev:Ey(s)}}};function zc(e){let t=e?.runtime?.flows;return!!(t?.getFlows&&t.getFlow&&t.setFlows)}a(zc,"canUseRedRuntime");function Iy(e){if(!e||typeof e!="object")return!1;let t=e;if((typeof t.status=="number"?t.status:t.statusCode)===404||t.code==="not_found"||t.code==="flow_not_found")return!0;let r=typeof t.message=="string"?t.message.toLowerCase():"";return r.includes("not found")||r.includes("unknown flow")}a(Iy,"isRuntimeFlowNotFound");y();var Gc=require("module");function Kc(e){if(!e||typeof e!="object")return!1;let t=e;return typeof t.getFlows=="function"&&typeof t.getFlow=="function"&&typeof t.setFlows=="function"}a(Kc,"isFlowsApi");function Ly(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Gc.createRequire)(e)}a(Ly,"createPluginRequire");function Ny(e){try{let t=e("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}a(Ny,"isNodeRedRuntimeInitialized");function Fy(){try{let e=Ly();if(!Ny(e))return null;let t=e("@node-red/runtime");return Kc(t.flows)?t.flows:null}catch{return null}}a(Fy,"tryRequireInitializedRuntimeFlows");function Jc(e){if(e?.runtime&&Kc(e.runtime.flows))return e;let t=Fy();return t?{runtime:{flows:t}}:null}a(Jc,"resolveRuntimeHost");function Vc(e={}){let t=Jc(e.RED??null);if(t&&zc(t))return new bs(t);let n=e.adminAuthEnabled===!0;if(e.http?.baseUrl){if(n&&!e.http.accessToken)throw new je("In-process Node-RED flow runtime is unavailable and HTTP Admin API fallback has no credentials while adminAuth is enabled. Ensure the plugin runs inside Node-RED so @node-red/runtime.flows can be used.");return new ws(e.http)}throw new je("No usable Node-RED flow adapter: in-process flows API is unavailable and no HTTP Admin API baseUrl was provided")}a(Vc,"createFlowAdapter");Ee();Us();y();var ks=require("fs"),Yc=require("path");co();de();var Oy=/^[a-z][a-z0-9._-]{0,63}$/;function $y(e){if(e.kind==="system")return{kind:"system"};if(e.kind==="access-link"){let n={kind:"access-link",linkId:String(e.linkId).replace(/\0/g,"").slice(0,200)};return typeof e.label=="string"&&(n.label=e.label.replace(/\0/g,"").slice(0,200)),n}let t={kind:"admin"};return typeof e.username=="string"&&(t.username=e.username.replace(/\0/g,"").slice(0,200)),t}a($y,"sanitizeActor");var vs=class{constructor(t,n={}){this.paths=t;this.filePath=(0,Yc.join)(t.logs,"audit.jsonl"),this.now=n.now??(()=>Date.now()),this.maxAppendBytes=n.maxAppendBytes??ha}static{a(this,"AuditLog")}filePath;now;maxAppendBytes;seq=0;append(t,n,r){let o=Oy.test(t)?t:"unknown";this.seq+=1;let s={id:`log-${this.now()}-${this.seq}`,at:new Date(this.now()).toISOString(),action:o,actor:$y(n)};r!==void 0&&(s.details=La(r));let i=St(s,this.maxAppendBytes,{kind:"audit"});return ga(this.paths.root,this.filePath,i),s}list(t=100){if(!(0,ks.existsSync)(this.filePath))return[];let n=(0,ks.readFileSync)(this.filePath,"utf8").split(`
|
|
7191
|
+
`).map(o=>o.trim()).filter(Boolean),r=[];for(let o of n)try{r.push(JSON.parse(o))}catch{}return r.slice(-Math.max(1,t)).reverse()}};tt();Qr();Re();Zt();Bo();Te();function My(e){let t=a(()=>Promise.reject(new Ze(e)),"fail");return{kind:"http",getFlows:t,getFlow:t,updateFlow:t}}a(My,"misconfiguredAdapter");function Zc(e){let t=e.settings,n=typeof t.uiPort=="number"?t.uiPort:1880,r=t.httpAdminRoot??"/",o=!r||r==="/"?"":r.replace(/\/$/,"");return`http://127.0.0.1:${n}${o}`}a(Zc,"localAdminBaseUrl");function As(e,t={}){let n=Kr(e.settings.userDir);ya(n);let r=t.settingsStore??new An(n,e.settings.aiAccess,u=>e.log?.warn?.(u));t.bindSettings!==!1&&to(r),ro(r.getEffective().publicBaseUrl);let o=t.linkStore??hs(e);o.configureDefaults({defaultTtlSeconds:r.getEffective().defaultLinkTtlSeconds,maxActiveLinks:r.getEffective().maxActiveLinks});let s=new po(n),i=new vs(n),d;if(t.flowAdapter)d=t.flowAdapter;else try{d=Vc({RED:e,http:{baseUrl:Zc(e)},adminAuthEnabled:ae(e.settings)})}catch(u){let p=u instanceof je?u.message:io;e.log?.warn?.(`AI Access flow adapter unavailable: ${p}`),d=My(p)}d=Ma(d);let l;if(t.nodeTypesAdapter)l=t.nodeTypesAdapter;else if(t.flowAdapter)l=Ks();else try{l=Ga({baseUrl:Zc(e)})}catch(u){let p=u instanceof Error?u.message:String(u);e.log?.warn?.(`AI Access node-types adapter unavailable: ${p}`),l=Ks([])}let c=new Uo(s,i,d,()=>Me(e.settings).defaultDeploymentType,e.settings.aiAccess);return{linkStore:o,proposalStore:s,auditLog:i,flowAdapter:d,nodeTypesAdapter:l,proposalService:c,settingsStore:r,...t.catalogueFetcher?{catalogueFetcher:t.catalogueFetcher}:{},...t.catalogueUrl?{catalogueUrl:t.catalogueUrl}:{}}}a(As,"createPluginServices");y();de();Re();Ye();Jr();y();var Xc=rr(require("dns"),1),Qc=rr(require("http"),1),eu=rr(require("https"),1),qr=require("net");Re();function Dy(e,t){return e===10||e===172&&t>=16&&t<=31||e===192&&t===168}a(Dy,"isPrivateV4");function Hy(e,t){return e===169&&t===254}a(Hy,"isLinkLocalV4");function jy(e,t,n){return e===0||e===100&&t>=64&&t<=127||e>=224||e===192&&t===0&&(n===0||n===2)||e===198&&(t===18||t===19||t===51&&n===100)||e===203&&t===0&&n===113}a(jy,"isSpecialUseV4");function Uy(e){let[t=0,n=0,r=0]=e.split(".").map(Number);return t===127?"loopback":Dy(t,n)||Hy(t,n)||jy(t,n,r)||e==="168.63.129.16"?"blocked":"public"}a(Uy,"classifyIpv4");function By(e){let t=e.split(":"),n=t.indexOf(""),r=[];for(let o of t){if(!o)continue;if(o.includes(".")){let i=o.split(".").map(Number);if(i.length!==4||i.some(d=>!Number.isInteger(d)||d<0||d>255))return;r.push((i[0]??0)<<8|(i[1]??0)),r.push((i[2]??0)<<8|(i[3]??0));continue}let s=Number.parseInt(o,16);if(!/^[\da-f]{1,4}$/i.test(o)||!Number.isInteger(s))return;r.push(s)}if(n>=0){let o=8-r.length;if(o<1)return;let s=t.slice(0,n).filter(Boolean).length;r.splice(s,0,...Array(o).fill(0))}else if(r.length!==8)return;return r.length===8?r:void 0}a(By,"expandIpv6");function qy(e){let t=By(e);if(!t)return"blocked";if(t.slice(0,7).every(s=>s===0)&&t[7]===1)return"loopback";let n=t[0]??0,r=t[1]??0;return n===0||n<8192||n>16383||n===8193&&r<=511||n===8193&&r===3512||n===16383&&r<=4095||n===8194?"blocked":"public"}a(qy,"classifyIpv6");function Wr(e){let t=Kt(e);return t?(0,qr.isIP)(t)===4?Uy(t):qy(t):"blocked"}a(Wr,"classifyPublicUrlAddress");function Wy(e){let t=e.toLowerCase().replace(/\.$/,"");return["metadata","metadata.google.internal","metadata.google.com","metadata.goog","instance-data","instance-data.ec2.internal","metadata.azure.internal","metadata.aws.internal"].some(n=>t===n||t.endsWith(`.${n}`))}a(Wy,"isMetadataHostname");async function tu(e,t,n){let r=xn(e);if(!r)throw new Error("publicBaseUrl is required for Check");let o=new URL(r),s=o.hostname.replace(/^\[|\]$/g,"");if(Wy(s))throw new Error("Public URL Check cannot target metadata hosts");let i=Kt(s),l=(typeof e=="string"?/^https?:\/\/(\[[^\]]+\]|[^/?#:]+)/i.exec(e.trim())?.[1]:void 0)?.replace(/^\[|\]$/g,"");if(i&&l&&l.toLowerCase()!==i.toLowerCase())throw new Error("Public URL Check requires canonical IP address notation");let c=s.toLowerCase().replace(/\.$/,"")==="localhost"||i!==void 0&&Wr(i)==="loopback",u=Number(o.port||(o.protocol==="https:"?443:80));if(c&&u!==t)throw new Error("Public URL Check may only target this Node-RED instance's port for loopback URLs");let p=a(g=>{let w=Wr(g);if(w==="blocked"||w==="loopback"&&!c||c&&w!=="loopback")throw new Error("Public URL Check cannot target private or non-public addresses")},"validateAddress");if(i)return p(i),{origin:r,address:i,family:(0,qr.isIP)(i)};let f=await new Promise((g,w)=>{let v=a(()=>w(n.reason instanceof Error?n.reason:new Error(String(n.reason))),"abort");n.throwIfAborted(),n.addEventListener("abort",v,{once:!0}),Xc.default.lookup(s,{all:!0},(k,$)=>{n.removeEventListener("abort",v),k?w(k):g($)})});if(n.throwIfAborted(),!f.length)throw new Error("Public URL Check hostname has no addresses");for(let g of f)p(g.address);let h=[];for(let g of f){let w=Kt(g.address);if(!w)throw new Error("Public URL Check returned an invalid address");h.some(v=>v.address===w)||h.push({address:w,family:(0,qr.isIP)(w)})}c&&h.sort((g,w)=>g.family-w.family);let m=h[0];if(!m)throw new Error("Public URL Check hostname has no addresses");return{origin:r,...m,addresses:h}}a(tu,"preparePublicUrlCheck");function zy(e,t,n){let r=e.hostname.replace(/^\[|\]$/g,"");return new Promise((o,s)=>{let d=(e.protocol==="https:"?eu.default:Qc.default).request(e,{method:"GET",agent:!1,signal:n,headers:{Host:e.host},...(0,qr.isIP)(r)?{}:{servername:r},lookup:a((l,c,u)=>{c.all?u(null,[{address:t.address,family:t.family}]):u(null,t.address,t.family)},"lookup")},l=>{o(l.statusCode??0),l.destroy()});d.on("error",s),d.end()})}a(zy,"requestPinned");async function nu(e,t,n){let r=new URL(e.origin+t),o=e.addresses?.length?e.addresses:[{address:e.address,family:e.family}],s;for(let i of o){n.throwIfAborted();try{return await zy(r,i,n)}catch(d){if(n.aborted)throw d;s=d}}throw s instanceof Error?s:new Error("Public URL Check request failed")}a(nu,"requestPublicUrlCheck");vn();Te();function Gy(e){return ur(e.settings.httpAdminRoot)}a(Gy,"mcpPageUrl");function Ky(e){return typeof e=="boolean"||typeof e=="number"||typeof e=="string"?e:Array.isArray(e)?e.join(", "):typeof e=="function"?"custom function":!1}a(Ky,"normalizeTrustProxy");function su(e){if(!e)return"Set a Public URL before enabling OAuth.";let t=new URL(e),n=Kt(t.hostname),r=t.hostname.toLowerCase().replace(/\.$/,"")==="localhost"||n==="::1"||n?.startsWith("127.");return t.protocol==="http:"&&!r?"OAuth Public URL uses HTTP; HTTPS is recommended.":null}a(su,"oauthPublicUrlWarning");function iu(e,t,n){let{sources:r,...o}=t.getEffective(),{permission:s}=no(e,n);return{ok:!0,adminAuthEnabled:ae(e.settings),canEdit:s==="*",permission:s,mcpPageUrl:Gy(e),apiBase:_e(e.settings.httpAdminRoot),settings:o,oauthPublicUrlWarning:su(o.publicBaseUrl),sources:r,disk:t.getDisk(),trustProxy:Ky(n.app.get("trust proxy")),precedence:["settings.js","disk","default"],defaults:{secretNamePatterns:[...Wt]}}}a(iu,"settingsResponse");function au(e,t){return(n,r)=>{r.json(iu(e,t,n))}}a(au,"getSettingsHandler");function ru(e,t,n){let r=e[t];if(r!==void 0){if(typeof r!="boolean")throw new Error(`${t} must be a boolean`);n[t]=r}}a(ru,"parseBooleanField");function ou(e,t,n){let r=e[t];if(r!==void 0){if(typeof r!="number"||!Number.isInteger(r)||r<1)throw new Error(`${t} must be an integer with a minimum of 1`);n[t]=Math.floor(r)}}a(ou,"parsePositiveIntField");function Jy(e,t,n){let r=e[t];if(r===void 0)return;let o=t==="rateLimitWindowMs"?100:1,s=t==="rateLimitWindowMs"?36e5:1e6;if(typeof r!="number"||!Number.isInteger(r)||r<o||r>s)throw new Error(`${t} must be an integer between ${o} and ${s}`);n[t]=r}a(Jy,"parseRateLimitField");function Vy(e,t){if(e.corsAllowedOrigins===void 0)return;if(!Array.isArray(e.corsAllowedOrigins))throw new Error("corsAllowedOrigins must be a list of HTTP(S) origins");let n=e.corsAllowedOrigins.map(r=>ir(r));if(n.some(r=>r===void 0))throw new Error("corsAllowedOrigins must contain only HTTP(S) origins without paths, credentials, queries, or fragments");t.corsAllowedOrigins=[...new Set(n)]}a(Vy,"parseCorsField");function Yy(e,t,n){let r=e.secretNamePatterns;if(r!==void 0){if(r===null){n.push("secretNamePatterns");return}if(!Array.isArray(r)||r.some(o=>typeof o!="string"))throw new Error("secretNamePatterns must be a list of strings (or null to reset)");if(r.length>ar)throw new Error(`secretNamePatterns allows at most ${ar} entries`);for(let o of r)if(!Vr.test(o.trim()))throw new Error('secretNamePatterns entries must be 2-40 letters, digits, "_" or "-" (e.g. API_KEY)');t.secretNamePatterns=dr(r)??[]}}a(Yy,"parseSecretPatternsField");function du(e,t,n){return(r,o)=>{let s=r.body;if(!s||typeof s!="object"||Array.isArray(s)){o.status(400).json({error:"Request body must be a settings object",code:"invalid_body"});return}let i={...s};for(let[c,u]of Object.entries(t.getEffective().sources))u==="settings.js"&&delete i[c];t.getEffective().sources.oauth==="settings.js"&&delete i.oauth;let d={},l=[];try{if(i.proposalTtlDays!==void 0){let c=i.proposalTtlDays;if(typeof c!="number"||!Number.isInteger(c)||c<1||c>365)throw new Error("proposalTtlDays must be an integer between 1 and 365");d.proposalTtlDays=c}if(i.oauth!==void 0){if(!i.oauth||typeof i.oauth!="object"||Array.isArray(i.oauth))throw new Error("oauth must be an object");let c=i.oauth,u={};if(c.enabled!==void 0){if(typeof c.enabled!="boolean")throw new Error("oauth.enabled must be a boolean");u.enabled=c.enabled}for(let f of["accessTokenTtlSec","refreshIdleTtlSec","dcrMaxClients"])if(c[f]!==void 0){if(typeof c[f]!="number"||!Number.isInteger(c[f])||c[f]<1||c[f]>31536e3)throw new Error(`oauth.${f} must be an integer with a minimum of 1`);u[f]=c[f]}let p=c.refreshMaxLifetimeSec;if(p!==void 0){if(typeof p!="number"||!Number.isInteger(p)||p<0||p>31536e4)throw new Error("oauth.refreshMaxLifetimeSec must be a non-negative integer");u.refreshMaxLifetimeSec=p}d.oauth=u}ru(i,"enabled",d),ru(i,"allowWithoutAdminAuth",d),ou(i,"defaultLinkTtlSeconds",d),ou(i,"maxActiveLinks",d)}catch(c){o.status(400).json({error:c.message,code:"invalid_body"});return}if(i.defaultDeploymentType!==void 0){let c=i.defaultDeploymentType;if(c!=="nodes"&&c!=="flows"&&c!=="full"){o.status(400).json({error:"defaultDeploymentType must be 'nodes', 'flows', or 'full'",code:"invalid_body"});return}d.defaultDeploymentType=c}try{i.publicBaseUrl!==void 0&&(d.publicBaseUrl=xn(i.publicBaseUrl)),Vy(i,d),Yy(i,d,l);for(let c of["rateLimitWindowMs","rateLimitMaxPerIp","rateLimitMaxPerToken"])Jy(i,c,d)}catch(c){o.status(400).json({error:c.message,code:"invalid_body"});return}try{let c=t.patch(d,l);ro(c.publicBaseUrl),n.configureDefaults({defaultTtlSeconds:c.defaultLinkTtlSeconds,maxActiveLinks:c.maxActiveLinks});let u=iu(e,t,r),p=c.publicBaseUrl?new URL(c.publicBaseUrl):void 0,f=p?Kt(p.hostname.replace(/^\[|\]$/g,"")):void 0,h=f?Wr(f):void 0,m=[];h!==void 0&&h!=="public"&&m.push("Public URL uses a private or non-public IP address.");let g=su(c.publicBaseUrl);g&&c.publicBaseUrl&&m.push(g),o.json({...u,...m.length?{warnings:m}:{}})}catch(c){if(c instanceof he){o.status(c.statusCode).json({error:c.message,code:c.code});return}z(o,c);return}}}a(du,"putSettingsHandler");function lu(e){return async(t,n)=>{let r=new AbortController,o=setTimeout(()=>r.abort(),5e3),s;try{let i;try{let c=e.settings,p=e.server?.address(),f=t.body?.publicBaseUrl,h=typeof f=="string"?new URL(f.trim()).hostname:"";if((h.toLowerCase().replace(/^\[|\]$/g,"").replace(/\.$/,"")==="localhost"||Wr(h.replace(/^\[|\]$/g,""))==="loopback")&&typeof p=="string")throw new Error("Public URL Check does not support loopback URLs when Node-RED listens on a Unix socket");let g=p&&typeof p=="object"?p.port:c.uiPort??1880;i=await tu(t.body?.publicBaseUrl,g,r.signal)}catch(c){if(r.signal.aborted)throw c;n.status(400).json({error:c.message,code:"invalid_body"});return}s=i.origin+_e(e.settings.httpAdminRoot);let d=await nu(i,_e(e.settings.httpAdminRoot),r.signal),l={status:d,ok:d>=200&&d<300};n.json({ok:l.ok,status:l.status,url:s,message:l.ok?"Discovery endpoint is reachable":`Discovery endpoint returned HTTP ${l.status}`})}catch{n.json({ok:!1,status:null,url:s,error:r.signal.aborted?"Public URL check timed out after 5 seconds":"Could not reach the discovery endpoint"})}finally{clearTimeout(o)}}}a(lu,"checkPublicUrlHandler");Te();Gt();Te();le();Qr();var Zy="com55-ai-access",xs=new WeakMap;function uu(e){let t=new Set;for(let n of String(e??"").split(",")){let[r,...o]=n.split(";").map(d=>d.trim().toLowerCase());if(!r)continue;let s=o.find(d=>d.startsWith("q=")),i=s?Number(s.slice(2)):1;Number.isFinite(i)&&i>0&&t.add(r)}return t}a(uu,"acceptedMediaTypes");function pu(e){let t=uu(e.headers.accept);return t.has("text/event-stream")&&!t.has("text/html")}a(pu,"isMcpStreamRequest");function Xy(e,t,n){e.httpAdmin.get("/mcp/api/v1",t,n,Ni(e)),e.httpAdmin.get("/mcp/api/v1/",t,n,Ni(e)),e.httpAdmin.get("/mcp/api/v1/openapi.yaml",t,n,_c()),e.httpAdmin.get("/mcp/api/v1/openapi.json",t,n,Cc()),e.httpAdmin.get("/mcp/editor/ai-access-sidebar.js",t,(r,o)=>{o.setHeader("Cache-Control","no-store, no-cache, must-revalidate, max-age=0"),o.setHeader("Pragma","no-cache"),o.setHeader("Expires","0"),o.setHeader("X-AI-Access-Sidebar-Version",Wn),o.type("application/javascript").status(200).send(qo())})}a(Xy,"registerMcpShellAndDiscovery");var Qy=/^\/mcp\/(?:api\/v1\/proposals|rpc$|a\/[^/]+\/(?:proposals|propose|draft))/;function fu(e,t={}){let n=As(e,{...t,linkStore:t.linkStore??hs(e)});$d(()=>n.settingsStore.getEffective().proposalTtlDays),xs.get(e)?.();let r=Dd(()=>_r(n.proposalStore,n.flowAdapter),500),o=a(A=>{A?.config?.rev&&Array.isArray(A.config.flows)&&Ts(we(n.flowAdapter),A.config)},"observeRuntimeDeploy"),s=a(()=>{r.cancel(),e.events?.removeListener?.("flows:stopping",o),e.events?.removeListener?.("flows:started",o),e.events?.removeListener?.("flows:started",r.trigger)},"cleanup");xs.set(e,s),e.events?.on("flows:stopping",o),e.events?.on("flows:started",o),e.events?.on("flows:started",r.trigger);let i=yl(()=>n.settingsStore.getEffective()),{ip:d,token:l}=i,c=new gs(Kr(e.settings.userDir),()=>n.settingsStore.getEffective().oauth.dcrMaxClients),u=a(()=>n.settingsStore.getEffective().publicBaseUrl,"publicBase"),p=a(()=>`${u().replace(/\/$/,"")}/mcp`,"mcpUrl"),f=new ys(c,p,()=>n.settingsStore.getEffective().oauth.accessTokenTtlSec,()=>n.settingsStore.getEffective().oauth.refreshIdleTtlSec,()=>n.settingsStore.getEffective().oauth.refreshMaxLifetimeSec),h=Sc(e,f,u,p);ba(e.httpAdmin,wa(()=>n.settingsStore.getEffective().corsAllowedOrigins));let m=a(A=>(Array.isArray(A)?A:A?[A]:[]).flatMap(I=>{if(typeof I=="string")return[I];let b=I;return b&&typeof b.path=="string"?[{path:b.path,root:typeof b.root=="string"?b.root:void 0}]:[]}),"staticMounts"),g={authorization:[],metadata:[],details:[],httpNodeRootFallback:e.settings.httpNodeRoot===!1||!e.httpNode},w="",v=a(()=>{let A=xc({httpAdmin:e.httpAdmin,httpNode:e.httpNode,httpAdminRoot:e.settings.httpAdminRoot,httpNodeRoot:e.settings.httpNodeRoot,metadataFallback:g.httpNodeRootFallback,eachNode:e.nodes?L=>e.nodes?.eachNode(L):void 0,staticDirs:m(e.settings.httpStatic),httpStaticRoot:e.settings.httpStaticRoot,fileExists:cu.existsSync}),I=A.authorization,b=A.metadata;g.authorization=I.map(oa),g.metadata=b.map(oa),g.details=[...I.map(L=>({...L,kind:"authorization"})),...b.map(L=>({...L,kind:"metadata"}))];let M=[...g.authorization.map(L=>`${L} \u2014 OAuth authorization routes disabled`),...g.metadata.map(L=>`${L} \u2014 OAuth metadata disabled`)],H=M.join(`
|
|
7192
|
+
`);if(H!==w)for(let L of M)e.log?.warn?.(`AI Access OAuth route collision: ${L}`);w=H},"refreshCollisions");v(),e.events?.on("flows:started",v);let k=Mi({windowMs:6e4,maxPerIp:30}),$=a((A,I,b)=>{if(!n.settingsStore.getEffective().oauth.enabled||g.authorization.length||!A.path.startsWith("/mcp/oauth/")){b();return}let M=A.path.replace(/\/+$/,"").toLowerCase(),H=a(()=>Sn(e)(A,I,()=>h.server(A,I,b)),"run");M==="/mcp/oauth/authorize"?d(A,I,()=>k(A,I,H)):M.startsWith("/mcp/oauth/consent/")?d(A,I,H):H()},"oauthServerMiddleware");$.oauthOwned=!0,e.httpAdmin.use($);let C=a((A,I,b)=>{if(!n.settingsStore.getEffective().oauth.enabled||g.metadata.length){b();return}h.metadata(A,I,b)},"metadataHandler");C.oauthOwned=!0,g.httpNodeRootFallback?e.httpAdmin.use("/mcp",C):e.httpNode?.use(C),e.httpAdmin.get("/mcp/api/v1/oauth/clients",d,l,Ce(e,"api"),(A,I)=>I.json({clients:c.data.clients.map(b=>({client_id:b.client_id,client_name:b.client_name,redirect_uris:b.redirect_uris,lastUsedAt:b.lastUsedAt}))})),e.httpAdmin.get("/mcp/api/v1/oauth/status",d,l,Ce(e,"api"),(A,I)=>{let b=!ae(e.settings)||Tt(A)==="*";I.json({...g,details:b?g.details:g.details.map(({file:M,...H})=>H),collisions:[...g.authorization,...g.metadata],oauthEnabled:n.settingsStore.getEffective().oauth.enabled})}),e.httpAdmin.delete("/mcp/api/v1/oauth/clients/:id",d,l,Ce(e,"api"),Rn(e),(A,I)=>I.json({revoked:c.revokeClient(String(A.params.id))})),e.httpAdmin.use((A,I,b)=>{if(!Qy.test(A.path)){b();return}n.flowAdapter.getFlows().then(M=>td(A,ko(M.flows))).catch(()=>{}).finally(()=>b())}),e.httpAdmin.use(ic(n.auditLog)),Ul(e,{linkStore:n.linkStore,proposalStore:n.proposalStore,auditLog:n.auditLog,flowAdapter:Lt(n.flowAdapter),nodeTypesAdapter:n.nodeTypesAdapter,...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{},rateLimiters:i}),e.httpAdmin.get("/ai-access",d,l,(A,I)=>{I.append("Content-Security-Policy","form-action 'none'; frame-ancestors 'none'"),I.setHeader("X-Frame-Options","DENY"),I.setHeader("Cache-Control","no-store, no-cache, must-revalidate, max-age=0"),I.setHeader("Pragma","no-cache"),I.setHeader("Expires","0"),I.type("html").status(200).send(Nc({apiBase:_e(e.settings.httpAdminRoot),mcpRpc:Rt(e.settings.httpAdminRoot),editorRoot:Pe(e.settings.httpAdminRoot),authTokenKey:so(e.settings.httpAdminRoot),dashboardPath:ur(e.settings.httpAdminRoot)}))}),e.httpAdmin.get("/mcp",d,(A,I,b)=>{if(pu(A)){b();return}I.setHeader("Cache-Control","no-store"),I.redirect(302,ur(e.settings.httpAdminRoot))}),Xy(e,d,l),e.httpAdmin.get("/mcp/api/v1/status",d,l,Ra(e,A=>n.linkStore.validateResult(A)),D((A,I)=>{I.json(Zn(e,A))})),e.httpAdmin.get("/mcp/api/v1/settings",d,l,Ce(e,"api"),D(au(e,n.settingsStore))),e.httpAdmin.put("/mcp/api/v1/settings",d,l,Ce(e,"api"),Rn(e),Fe("rest"),D(du(e,n.settingsStore,n.linkStore))),e.httpAdmin.post("/mcp/api/v1/settings/check-public-url",d,l,Ce(e,"api"),Rn(e),Fe("rest"),D(lu(e)));let O=[d,l,Ce(e,"api"),Sn(e),Rn(e)];e.httpAdmin.get("/mcp/api/v1/links",...O,D(Yl(e,n.linkStore))),e.httpAdmin.post("/mcp/api/v1/links",...O,D(Zl(e,n.linkStore,n.auditLog))),e.httpAdmin.delete("/mcp/api/v1/links/:id",...O,D(Xl(e,n.linkStore,n.auditLog)));let q=[d,l,Sn(e),Sa(e,A=>n.linkStore.validateResult(A))],T=n.flowAdapter,S=n.nodeTypesAdapter,F={nodeTypes:S,...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{}};e.httpAdmin.get("/mcp/api/v1/flows",...q,D(al(T))),e.httpAdmin.get("/mcp/api/v1/flows/:id",...q,D(ts(T))),e.httpAdmin.get("/mcp/api/v1/nodes/:id",...q,D(ns(T))),e.httpAdmin.get("/mcp/api/v1/nodes/:id/field/:name",...q,D(rs(T))),e.httpAdmin.get("/mcp/api/v1/search",...q,D(os(T))),e.httpAdmin.get("/mcp/api/v1/node-types",...q,D(ss(F))),e.httpAdmin.get("/mcp/api/v1/node-types/:type",...q,D(is(F))),e.httpAdmin.get("/mcp/api/v1/palette/search",...q,D(as(F)));let N=e.settings.httpAdminRoot;e.httpAdmin.get("/mcp/api/v1/proposals",...q,D(Oc(n.proposalStore,n.flowAdapter,N))),e.httpAdmin.get("/mcp/api/v1/proposals/:id",...q,D($c(n.proposalStore,N,n.flowAdapter))),e.httpAdmin.get("/mcp/api/v1/proposals/:id/diff",...q,D(Uc(n.proposalStore,n.flowAdapter))),e.httpAdmin.post("/mcp/api/v1/proposals",d,l,Fe("rest"),Pn(e,n.linkStore,"rest"),D(Fc(n.proposalStore,n.auditLog,n.flowAdapter,N,n.nodeTypesAdapter))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/approve",...O,Fe("rest"),D(Mc(n.proposalService,N))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/confirm-live-checked",...O,Fe("rest"),D(Dc(n.proposalService,N))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/reject",...O,Fe("rest"),D(Hc(n.proposalService,N))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/withdraw",...q,(A,I,b)=>{A.accessLink?b():Ce(e,"api")(A,I,()=>Rn(e)(A,I,b))},Fe("rest"),D(jc(n.proposalStore,n.auditLog,N))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/resolve-conflict",...O,Fe("rest"),D(qc(n.proposalStore,n.auditLog,T,N))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/recheck",...O,D(Wc(n.proposalStore,T,N))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/resolve",d,l,Fe("rest"),Pn(e,n.linkStore,"rest"),D(Bc(n.proposalStore,n.auditLog,T,N,S)));let P=[d,l,(A,I,b)=>{let M=A.headers.authorization;if(!n.settingsStore.getEffective().oauth.enabled||M?.toLowerCase().startsWith("bearer aia_")){if(n.settingsStore.getEffective().oauth.enabled){let H=I.setHeader.bind(I);I.setHeader=((L,ee)=>{if(L.toLowerCase()==="www-authenticate"&&typeof ee=="string"&&!ee.includes("resource_metadata=")){let Se=`${u().replace(/\/$/,"")}${g.httpNodeRootFallback?"/mcp":""}/.well-known/oauth-protected-resource/mcp`;ee+=`, resource_metadata="${Se}", scope="read propose"`}return H(L,ee)})}Pn(e,n.linkStore,"mcp")(A,I,b);return}Sn(e)(A,I,()=>h.auth(A,I,b))}];e.httpAdmin.post("/mcp/rpc",...P,Fe("mcp"),D(Zi(e,n))),e.httpAdmin.get("/mcp/rpc",...P,jr),e.httpAdmin.delete("/mcp/rpc",...P,jr),e.httpAdmin.post("/mcp",...P,Fe("mcp"),D(Zi(e,n))),e.httpAdmin.get("/mcp",...P,jr),e.httpAdmin.delete("/mcp",...P,jr),e.httpAdmin.get("/mcp/api/v1/logs",d,l,Ce(e,"api"),Sn(e),D(Ql(n.auditLog,n.linkStore))),e.httpAdmin.use("/mcp/api/v1",ol()),zl(e)}a(fu,"registerRoutes");function ew(e){e.plugins.registerPlugin(Zy,{type:"com55-ai-access",onremove(){xs.get(e)?.(),xs.delete(e)},onadd(){fu(e),e.log?.info?.("Node-RED AI Access plugin loaded (dashboard /ai-access, MCP /mcp)")}})}a(ew,"registerAiAccessPlugin");var tw=ew;0&&(module.exports={ACCESS_LINK_INVALID_MESSAGE,ACCESS_LINK_MISSING_MESSAGE,AccessLinkStore,EDITOR_SIDEBAR_ASSET_VERSION,MAX_PROPOSAL_JSON_BYTES,SettingsStore,acceptedMediaTypes,areAiFeaturesEnabled,bindSettingsStore,createPluginServices,getStorageDir,isAdminAuthEnabled,isMcpStreamRequest,packageVersion,registerRoutes,renderEditorSidebarJs,renderEditorSidebarLoaderHtml,requireAccessLink,resolveAiAccessSettings});
|