@com55/node-red-ai-access 0.1.0 → 0.1.1
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 +69 -0
- package/README.md +64 -0
- package/ai-access.html +1 -1
- package/dist/plugin.cjs +94 -78
- package/dist/plugin.d.cts +15 -1
- package/docs/README.md +2 -0
- package/docs/api/openapi.yaml +16 -6
- package/docs/errors.md +1 -1
- package/docs/releasing.md +82 -0
- package/package.json +1 -1
package/dist/plugin.cjs
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var sa=Object.create;var Ft=Object.defineProperty;var ia=Object.getOwnPropertyDescriptor;var aa=Object.getOwnPropertyNames;var da=Object.getPrototypeOf,la=Object.prototype.hasOwnProperty;var a=(e,t)=>Ft(e,"name",{value:t,configurable:!0});var C=(e,t)=>()=>(e&&(t=e(e=0)),t);var mo=(e,t)=>{for(var n in t)Ft(e,n,{get:t[n],enumerable:!0})},go=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of aa(t))!la.call(e,o)&&o!==n&&Ft(e,o,{get:()=>t[o],enumerable:!(r=ia(t,o))||r.enumerable});return e};var ca=(e,t,n)=>(n=e!=null?sa(da(e)):{},go(t||!e||!e.__esModule?Ft(n,"default",{value:e,enumerable:!0}):n,e)),pa=e=>go(Ft({},"__esModule",{value:!0}),e);function tt(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 M(n.kind,o.byteLength,t);return{bytes:o}}function go(e,t){let n=(0,Ft.resolve)(t),r=(0,Ft.resolve)(e);if(r!==n&&!r.startsWith(n+Ft.sep))throw new Error("Path escapes .ai-access storage root");return r}function dn(e,t,n){let r=go(t,e),o=`${r}.${process.pid}.tmp`;(0,ke.writeFileSync)(o,n.bytes,{mode:an}),(0,ke.renameSync)(o,r),(0,ke.chmodSync)(r,an)}function ho(e,t,n){let r=go(t,e);(0,ke.appendFileSync)(r,n.bytes,{mode:an});try{(0,ke.chmodSync)(r,an)}catch{}}var ke,Ft,pr,an,E,mo,M,$=C(()=>{"use strict";ke=require("fs"),Ft=require("path"),pr=448,an=384,E=15e5,mo=8192,M=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(tt,"encodeTrustedJson");a(go,"assertUnderAiAccess");a(dn,"writeTrustedAtomic");a(ho,"appendTrusted")});function ea(e){return e==="nodes"||e==="flows"||e==="full"}function ln(e){if(!e||typeof e!="object"||Array.isArray(e))return{};let t=e,n={};return typeof t.enabled=="boolean"&&(n.enabled=t.enabled),typeof t.allowWithoutAdminAuth=="boolean"&&(n.allowWithoutAdminAuth=t.allowWithoutAdminAuth),typeof t.defaultLinkTtlSeconds=="number"&&Number.isFinite(t.defaultLinkTtlSeconds)&&t.defaultLinkTtlSeconds>0&&(n.defaultLinkTtlSeconds=Math.floor(t.defaultLinkTtlSeconds)),ea(t.defaultDeploymentType)&&(n.defaultDeploymentType=t.defaultDeploymentType),typeof t.maxActiveLinks=="number"&&Number.isFinite(t.maxActiveLinks)&&t.maxActiveLinks>0&&(n.maxActiveLinks=Math.floor(t.maxActiveLinks)),n}function ta(e){if(!(0,pn.existsSync)(e))return{};try{let t=(0,pn.readFileSync)(e,"utf8");return ln(JSON.parse(t))}catch{return{}}}function cn(e,t){let n={enabled:"default",allowWithoutAdminAuth:"default",defaultLinkTtlSeconds:"default",defaultDeploymentType:"default",maxActiveLinks:"default"},r=a((o,s)=>t[o]!==void 0?(n[o]="disk",t[o]):e[o]!==void 0?(n[o]="settings.js",e[o]):(n[o]="default",s),"pick");return{enabled:r("enabled",Dt.enabled),allowWithoutAdminAuth:r("allowWithoutAdminAuth",Dt.allowWithoutAdminAuth),defaultLinkTtlSeconds:r("defaultLinkTtlSeconds",Dt.defaultLinkTtlSeconds),defaultDeploymentType:r("defaultDeploymentType",Dt.defaultDeploymentType),maxActiveLinks:r("maxActiveLinks",Dt.maxActiveLinks),sources:n}}function ur(e){return ln(e??{})}var pn,yo,Zi,Dt,nt,un=C(()=>{"use strict";pn=require("fs"),yo=require("path");$();Zi=16384,Dt={enabled:!0,allowWithoutAdminAuth:!1,defaultLinkTtlSeconds:14400,maxActiveLinks:10,defaultDeploymentType:"nodes"};a(ea,"isDeploymentType");a(ln,"sanitizePartial");a(ta,"readDiskFile");a(cn,"mergeAiAccessSettings");a(ur,"settingsJsPartial");nt=class{constructor(t,n){this.paths=t;this.filePath=(0,yo.join)(t.root,"settings.json"),this.settingsJs=ur(n),this.disk=ta(this.filePath),this.cached=cn(this.settingsJs,this.disk)}static{a(this,"SettingsStore")}filePath;disk;settingsJs;cached;getEffective(){return this.cached}getDisk(){return{...this.disk}}update(t){let n=ln(t);return this.disk=n,this.persist(),this.cached=cn(this.settingsJs,this.disk),this.cached}patch(t){return this.update({...this.disk,...ln(t)})}persist(){let t=tt(this.disk,Zi,{kind:"settings",pretty:!0});dn(this.paths.root,this.filePath,t)}}});function fn(e){fr=e}function Y(e){return e.adminAuth!=null&&e.adminAuth!==!1}function se(e){if(fr){let n=fr.getEffective();return{enabled:n.enabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,defaultLinkTtlSeconds:n.defaultLinkTtlSeconds,defaultDeploymentType:n.defaultDeploymentType,maxActiveLinks:n.maxActiveLinks}}let t=cn(ur(e.aiAccess),{});return{enabled:t.enabled,allowWithoutAdminAuth:t.allowWithoutAdminAuth,defaultLinkTtlSeconds:t.defaultLinkTtlSeconds,defaultDeploymentType:t.defaultDeploymentType,maxActiveLinks:t.maxActiveLinks}}function ie(e){let t=se(e);return t.enabled?Y(e)?!0:t.allowWithoutAdminAuth:!1}var fr,Ae=C(()=>{"use strict";un();fr=null;a(fn,"bindSettingsStore");a(Y,"isAdminAuthEnabled");a(se,"resolveAiAccessSettings");a(ie,"areAiFeaturesEnabled")});function z(e){return!e||e==="/"?"/":e.endsWith("/")?e.slice(0,-1):e}function me(e){let t=z(e);return t==="/"?"/mcp/api/v1":`${t}/mcp/api/v1`}function Pe(e){let t=z(e);return t==="/"?"/mcp/rpc":`${t}/mcp/rpc`}function na(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 rt(e){let t=na(e);return t.includes("*")?"*":t.includes("read")?"read":null}function ra(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 wo(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 Ot(e,t){let n="read";return(r,o,s)=>{if(!Y(e.settings)){s();return}let i=t==="api"?wo(o):null;e.auth.needsPermission(n)(r,o,d=>{if(i?.(),d){s(d);return}if(!r.user){if(t==="page"){o.redirect(z(e.settings.httpAdminRoot));return}o.status(401).json({error:"Authentication required",code:"auth_required"});return}if(!rt(r)){o.status(401).json({error:"Insufficient permissions",code:"insufficient_permissions"});return}s()})}}function bo(e,t){let n=Y(e.settings),r=se(e.settings),o=ie(e.settings),s=null;return n?s=rt(t):o&&(s="*"),{adminAuthEnabled:n,aiFeaturesEnabled:o,allowWithoutAdminAuth:r.allowWithoutAdminAuth,permission:s,capabilities:ra(s,o)}}function mn(e){return(t,n,r)=>{if(!ie(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 mr(e){return(t,n,r)=>{if(!Y(e.settings)){if(ie(e.settings)){r();return}n.status(403).json({error:"AI Access features are disabled",code:"ai_features_disabled"});return}if(rt(t)!=="*"){n.status(403).json({error:"Full admin permission (*) is required for this action",code:"insufficient_permissions"});return}r()}}function oa(e){return e.startsWith("aia_")}function sa(e){return oa(e)?!0:e.length<24}function ia(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 vo(e,t,n,r){if(!Y(e.settings)){r();return}let o={error:Q,code:ge},s=wo(n,o,j);e.auth.needsPermission("read")(t,n,i=>{if(s(),i){r(i);return}if(!n.headersSent){if(!t.user||!rt(t)){n.setHeader("WWW-Authenticate",j),n.status(401).json(o);return}r()}})}function ko(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?(jt(t,r,l.link.kind),"wrong_kind"):(e.accessLink=l.link,"ok");if(l.status==="expired")return $t(e,t,o,l.expiresAt),"invalid";if(sa(d)){t.setHeader("WWW-Authenticate",j);let c=ia(d);return t.status(401).json({error:Oe,code:Mt,...c?{hint:c}:{}}),"invalid"}return"continue"}function Ao(e,t,n="rest"){return(r,o,s)=>{if(!ie(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=ko(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||vo(e,r,o,s)}}function So(e,t,n="rest"){return(r,o,s)=>{let i=ko(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||vo(e,r,o,s)}}var Se=C(()=>{"use strict";Me();Ae();a(z,"normalizeHttpAdminRoot");a(me,"mcpApiBase");a(Pe,"mcpRpcPath");a(na,"scopesFromRequest");a(rt,"resolvePermission");a(ra,"capabilitiesFor");a(wo,"interceptNeedsPermissionUnauthorized");a(Ot,"requireAdminSession");a(bo,"describeAccess");a(mn,"requireAiFeatures");a(mr,"requireStarPermission");a(oa,"looksLikeAccessLinkToken");a(sa,"looksLikeFailedAccessLinkAttempt");a(ia,"truncatedTokenHint");a(vo,"continueAsAdminSessionOrAccessLinkMissing");a(ko,"tryAccessLinkBearer");a(Ao,"requireAdminReadOrAccessLink");a(So,"requireAdminSessionOrAccessLink")});function aa(e,t){let n=z(e);return`${n==="/"?"/mcp/a":`${n}/mcp/a`}/${t}`}function da(e){return!!e.app.get("trust proxy")}function la(e){let t=e.get("x-forwarded-host");if(typeof t!="string"||t.length===0)return;let n=t.split(",")[0]?.trim();return n&&n.length>0?n:void 0}function ca(e){let n=da(e)?la(e)??e.get("host"):e.get("host");if(n)return`${e.protocol}://${n}`}function Ht(e,t){let n=ca(e);return n?`${n}${t}`:t}function gr(e,t,n){return Ht(e,aa(t,n))}function hr(e){return e.replace(/\/mcp\/a\/[^/?#]+/gi,"/mcp/a/<redacted>")}var ot,xo,$e=C(()=>{"use strict";Se();ot=8192,xo=1800*1e3;a(aa,"mcpAccessLinkPath");a(da,"isTrustProxyEnabled");a(la,"firstForwardedHost");a(ca,"requestPublicOrigin");a(Ht,"absolutizePath");a(gr,"buildAccessLinkUrl");a(hr,"redactAccessLinkTokenInPath")});function ha(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 yr(e,t,n,r){e.setHeader("WWW-Authenticate",j),e.status(401).json({error:t,code:n,...r?{hint:r}:{}})}function ya(e){let t=e??"";for(;t.endsWith("/");)t=t.slice(0,-1);return`${t}/mcp#links`}function wa(e,t,n,r){let o=Ht(e,ya(t));return{error:pa,code:fa,...n?{expiredAt:n}:{},accessLinksUrl:o,hint:r??`Ask the user to create a new access link in Node-RED on the /mcp page \u2192 Access links (${o}).`}}function $t(e,t,n,r,o){t.setHeader("WWW-Authenticate",j),t.status(401).json(wa(e,n,r,o))}function ba(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 gn(e){return e==="get_link"?"Please open Node-RED \u2192 /mcp \u2192 Access links and create a get_link access URL, then send me that /mcp/a/<token> link.":`Please open Node-RED \u2192 /mcp \u2192 Access links and create a kind=${e} token, then send me the Bearer token (not a get_link path URL).`}function ka(e,t){let n=va;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: ${gn("get_link")}`:e==="rest"?`This REST endpoint requires a rest token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${gn("rest")}`:`This MCP endpoint requires an mcp token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${gn("mcp")}`}function jt(e,t,n){e.setHeader("WWW-Authenticate",j),e.status(403).json({error:`Access link kind mismatch: expected ${t}, got ${n}`,code:ga,expectedKind:t,actualKind:n,hint:ka(t,n),tellUser:gn(t)})}function st(e,t,n){return(r,o,s)=>{if(!ie(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=ha(r.headers.authorization);if(i.kind==="basic"){yr(o,ua,ma);return}if(i.kind==="missing"||i.kind==="other"){yr(o,Q,ge);return}let d=t.validateResult(i.token);if(d.status==="expired"){$t(r,o,e.settings.httpAdminRoot,d.expiresAt);return}if(d.status!=="active"){yr(o,Oe,Mt,ba(i.token));return}if(d.link.kind!==n){jt(o,n,d.link.kind);return}r.accessLink=d.link,s()}}var Oe,pa,Q,ua,j,ge,Mt,fa,ma,ga,va,Me=C(()=>{"use strict";$e();Ae();Oe="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.",pa="Access link token has expired. Ask the user for a new access link.",Q="Access link Bearer token is missing. Ask the user for a new access link and send Authorization: Bearer <token>.",ua="Authorization must use the Bearer scheme (Authorization: Bearer <access-link-token>), not Basic. Ask the user for a new access link.",j='Bearer realm="node-red-ai-access", charset="UTF-8"',ge="access_link_missing",Mt="access_link_invalid",fa="token_expired",ma="bearer_scheme_required",ga="wrong_token_kind";a(ha,"parseAuthorizationHeader");a(yr,"sendUnauthorized");a(ya,"accessLinksPagePath");a(wa,"tokenExpiredBody");a($t,"sendTokenExpired");a(ba,"truncatedBearerHint");va="ask the owner: Node-RED /mcp \u2192 Access links \u2192 create kind=rest/mcp (or get_link)";a(gn,"whatToTellUserWrongTokenKind");a(ka,"wrongTokenKindHint");a(jt,"sendWrongTokenKind");a(st,"requireAccessLink")});var F,I,ae,he,hn,de=C(()=>{"use strict";F=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}},I=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}},ae=class extends Error{static{a(this,"AdapterUnavailableError")}code="adapter_unavailable";statusCode=502;constructor(t){super(t),this.name="AdapterUnavailableError"}},he=class extends Error{static{a(this,"PluginMisconfiguredError")}code="plugin_misconfigured";statusCode=502;constructor(t){super(t),this.name="PluginMisconfiguredError"}},hn="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){if(!e||!Aa.test(e)||e.includes(".."))throw new Error(`Invalid ${t}`);return e}function yn(e){if(!e||!Sa.test(e)||e.includes(".."))throw new Error("Invalid proposal id");return e}function wr(e,t){let n=(0,je.basename)(t);if(!n||n!==t||n.includes(".."))throw new Error("Invalid file name");let r=(0,je.resolve)(e),o=(0,je.resolve)(r,n);if(o!==r&&!o.startsWith(r+je.sep))throw new Error("Path escapes storage directory");return o}function Po(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 xa){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=Po(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>E)throw new M(`flow.${o}`,d,E);n[o]=JSON.parse(i)}let r=Buffer.byteLength(JSON.stringify(n),"utf8");if(r>E)throw new M("flow",r,E);return n}function _o(e){let t={};for(let n of Ta){if(!(n in e))continue;let r=e[n];if(Pa.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=Po(r,2e3);o!==void 0&&(t[n]=o)}return t}var je,Aa,Sa,xa,Ta,Pa,wn=C(()=>{"use strict";je=require("path");$();Aa=/^[A-Za-z0-9_.-]+$/,Sa=/^prop-[A-Za-z0-9_-]+$/,xa=["id","type","label","disabled","info","env"],Ta=["proposalId","flowId","expectedRev","resultRev","reason","code","currentRev","title","displayName","flowLabel","linkId","label","expiresAt","createdAt","ttlSeconds","kind"],Pa=new Set(["ttlSeconds"]);a(xe,"assertSafeSegment");a(yn,"assertSafeProposalId");a(wr,"resolveUnderDir");a(Po,"sanitizeString");a(it,"sanitizeFlowTab");a(_o,"sanitizeAuditDetails")});function _a(e){return e==="pending"||e==="approved"||e==="rejected"||e==="failed"||e==="withdrawn"||e==="expired"||e==="superseded"}function Ro(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e.slice(0,Ra)){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,_e,Lo,J,G,at,Bt,Ra,bn,dt=C(()=>{"use strict";Eo=require("crypto"),_e=require("fs"),Lo=require("path");wn();$();J=class extends Error{static{a(this,"ProposalNotFoundError")}code="proposal_not_found";statusCode=404;constructor(t){super(`Proposal '${t}' was not found`),this.name="ProposalNotFoundError"}},G=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"}},at=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"}},Bt=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(_a,"isProposalStatus");Ra=50;a(Ro,"sanitizeProposalWarnings");bn=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??E}static{a(this,"ProposalStore")}inFlight=new Set;now;randomId;maxProposalBytes;fileFor(t){let n;try{n=yn(t)}catch{throw new J(t)}return wr(this.paths.proposals,`${n}.json`)}toDiskRecord(t){let n=yn(t.id),r=xe(String(t.expectedRev).slice(0,200),"expectedRev"),o=xe(String(t.payload.flowId).slice(0,200),"flowId"),s=it(t.payload.flow),i=t.baseFlow!==void 0?it(t.baseFlow):void 0,d={kind:"access-link",linkId:xe(String(t.createdBy.linkId).slice(0,200),"linkId")};typeof t.createdBy.label=="string"&&(d.label=t.createdBy.label.replace(/\0/g,"").slice(0,200));let l={id:n,type:"replace_flow",status:_a(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=Ro(t.warnings);if(c&&(l.warnings=c),t.decidedBy)if(t.decidedBy.kind==="access-link"){let p={kind:"access-link",linkId:xe(String(t.decidedBy.linkId).slice(0,200),"linkId")};typeof t.decidedBy.label=="string"&&(p.label=t.decidedBy.label.replace(/\0/g,"").slice(0,200)),l.decidedBy=p}else{let p={kind:"admin"};typeof t.decidedBy.username=="string"&&(p.username=t.decidedBy.username.replace(/\0/g,"").slice(0,200)),l.decidedBy=p}return typeof t.reason=="string"&&(l.reason=t.reason.replace(/\0/g,"").slice(0,2e3)),typeof t.resultRev=="string"&&(l.resultRev=xe(t.resultRev.slice(0,200),"resultRev")),l}writeAtomic(t,n){let r=this.fileFor(t),o=this.toDiskRecord(n),s=tt(o,this.maxProposalBytes,{kind:"proposal",pretty:!0});dn(this.paths.root,r,s)}create(t){let n=new Date(this.now()).toISOString(),r=yn(`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=Ro(t.warnings);return s&&(o.warnings=s),this.writeAtomic(r,o),this.get(r)}get(t){let n=this.fileFor(t);if(!(0,_e.existsSync)(n))throw new J(t);return JSON.parse((0,_e.readFileSync)(n,"utf8"))}list(t){if(!(0,_e.existsSync)(this.paths.proposals))return[];let n=(0,_e.readdirSync)(this.paths.proposals).filter(o=>o.endsWith(".json")),r=[];for(let o of n){let s=(0,Lo.basename)(o);if(!(s!==o||s.includes("..")))try{let i=JSON.parse((0,_e.readFileSync)(wr(this.paths.proposals,s),"utf8"));if(t?.linkId&&i.createdBy.linkId!==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 Bt(t);let n=this.get(t);if(n.status!=="pending")throw new G(t,n.status);return this.inFlight.add(t),n}releaseLock(t){this.inFlight.delete(t)}markApproved(t,n,r){let o=this.get(t);return o.status="approved",o.decidedBy=n,o.resultRev=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markRejected(t,n,r){let o=this.get(t);return o.status="rejected",n&&(o.decidedBy=n),o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markFailed(t,n,r){let o=this.get(t);return o.status="failed",n&&(o.decidedBy=n),o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markWithdrawn(t,n,r){let o=this.get(t);if(o.status!=="pending")throw new G(t,o.status);return o.status="withdrawn",o.decidedBy=n,o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markClosed(t,n,r,o){let s=this.get(t);if(s.status!=="pending")throw new G(t,s.status);return s.status=n,o&&(s.decidedBy=o),s.reason=r,s.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,s),this.get(t)}}});function Co(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 vn(e,t,n){let r=e.find(d=>d.id===t);if(!r||r.type!=="tab"&&r.type!=="subflow")throw new I(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({...Co(l,`flowData.nodes[${d}]`),z:t});for(let[d,l]of(n.configs??[]).entries())s.push({...Co(l,`flowData.configs[${d}]`),z:t});return[...e.filter(d=>d.id!==t&&d.z!==t),o,...s]}function qt(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 lt(e,t){if(Array.isArray(t)){let d=t.find(p=>p&&typeof p=="object"&&p.id===e&&(p.type==="tab"||p.type==="subflow"));if(!d)throw new I(e);let l=t.filter(p=>p&&typeof p=="object"&&p.id!==e&&p.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 I(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 kn=C(()=>{"use strict";de();a(Co,"asFlowNode");a(vn,"replaceFlowTabInFlatFlows");a(qt,"assertFlowsSnapshot");a(lt,"normalizeFlowTab")});function Ar(e){return e?Ea.has(e)?!0:e.startsWith("subflow:"):!1}function Re(e,t="node.type"){if(e==null||e==="")throw new g(`${t} is required`,"missing_node_type");if(typeof e!="string")throw new g(`${t} must be a non-empty string (got ${typeof e})`,"invalid_node_type",{details:{type:e,got:typeof e}});if(!e.trim())throw new g(`${t} is required`,"missing_node_type");return e}function Oa(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 p=1;p<=s;p++){let u=c===r.charAt(p-1)?0:1;d[p]=Math.min((d[p-1]??0)+1,(i[p]??0)+1,(i[p-1]??0)+u)}for(let p=0;p<=s;p++)i[p]=d[p]??0}return i[s]??s}function Ma(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 ja(e){return Math.max(2,Math.floor(e.length/3))}function Sr(e,t,n=5){if(typeof e!="string"||!e)return[];let r=ja(e);return t.filter(s=>typeof s=="string"&&s!==e&&!$a.has(s)&&!s.startsWith("_")&&!s.startsWith("subflow:")).map(s=>({c:s,dist:Oa(e,s),prefix:Ma(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 Ha(e){return e.endsWith("/")?e.slice(0,-1):e}function $o(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Mo.createRequire)(e)}function Ba(){try{let t=$o()("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}function jo(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 xr(e=Da,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 Ho(e={}){if(Ba())try{let n=$o()("@node-red/runtime");if(n.nodes&&typeof n.nodes.getNodeList=="function")return new kr(n.nodes)}catch{}if(e.baseUrl)return new vr({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 Tr(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 qa(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 p=/^(#{1,3})\s*(inputs?|outputs?)\b[\s:]*\n?([\s\S]*)$/i.exec(c);if(p){let u=(p[2]??"").toLowerCase(),f=(p[3]??"").trim();u.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 Va(e){return e==="_DEFAULT_"?"":e}function An(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=Ka(t,o+1);s+=d.char,o=d.next;continue}if(i===r)break;s+=i,o+=1}return Va(s)}if(t.startsWith("[")){let r=Bo(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=An(d);s.push(l);let c=br(d);i+=c>0?c:1}return s}if(t.startsWith("{")){let r=Ut(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=An(o.slice(i));d=typeof c=="string"?c:"",i+=br(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]=An(l),i+=br(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 br(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=Bo(t);return n+(o?.length??1)}if(t.startsWith("{")){let o=Ut(t);return n+(o?.length??1)}let r=/^(?:-?\d+(?:\.\d+)?|true|false|null)/.exec(t);return r?n+r[0].length:n+1}function Bo(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 Xa(e){return An(e)}function Ya(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 Pr(e,t){let n=e.toLowerCase();return t.includes(e)?e:t.find(r=>r.toLowerCase()===n)}async function He(e,t,n={}){if(!t||typeof t!="string")throw new g("Missing node type","missing_node_type");let r=n.includeHelp!==!1;if(Ar(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=Ya(o,t);if(!i){let l=Pr(t,s);throw l&&l!==t?new g(`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 g(`Node type '${t}' was not found`,"node_type_not_found",{statusCode:404,details:{type:t,nearest:Sr(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=Wa(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=za(c.help)))}}catch{}return d}async function qo(e,t){let{allNames:n}=await Tr(e),r=new Set(n),o=new Set,s=a(l=>{let c=Re(l,"node.type");if(o.has(c)||Ar(c)){o.add(c);return}if(o.add(c),r.has(c))return;let p=Pr(c,n);throw p&&p!==c?new g(`Unknown node type '${c}' \u2014 did you mean '${p}'? Types are case-sensitive.`,"unknown_node_type",{details:{type:c,didYouMean:p,caseSensitive:!0,nearest:[p]}}):new g(`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:Sr(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 p of c.nodes)i(p);if(Array.isArray(c.configs))for(let p of c.configs)i(p)},"visitTab")(t)}async function Uo(e,t){let n=Re(t,"node.type");if(Ar(n))return;let{allNames:r}=await Tr(e);if(r.includes(n))return;let o=Pr(n,r);throw o&&o!==n?new g(`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 g(`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:Sr(n,r,5)}})}async function Qa(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 Za(e,t){let n=Date.now();if(le&&n-le.fetchedAt<Fa)return le.body;let r=le?.etag,o=await e(t,r);if(o.status===304&&le)return le={...le,fetchedAt:n,...o.etag?{etag:o.etag}:{}},le.body;if(!o.body||!Array.isArray(o.body.modules)){if(le)return le.body;throw new g("Node-RED catalogue unavailable","catalogue_unavailable",{statusCode:502})}return le={fetchedAt:n,body:o.body,...o.etag?{etag:o.etag}:{}},o.body}async function xn(e,t,n={}){let r=t.trim();if(!r)throw new g("q query parameter is required","missing_query");let o=n.limit??Ca,s=n.catalogueUrl??Ia,i=n.catalogueFetcher??Qa,d=await Za(i,s),l=Array.isArray(d.modules)?d.modules:[],c=await e.listNodeSets(),p=new Set(c.flatMap(y=>{let x=[y.module,y.id];return y.id.includes("/")&&x.push(y.id.split("/")[0]??y.module),x.map(k=>k.toLowerCase())})),u=r.toLowerCase(),f=[];for(let y of l){let x=y.id.toLowerCase(),k=(y.keywords??[]).map(Nt=>String(Nt).toLowerCase()),re=(y.description??"").toLowerCase(),U=ed(u,x,k,re);if(U<=0)continue;let L={module:y.id,installed:p.has(x)};typeof y.description=="string"&&(L.description=y.description),typeof y.version=="string"&&(L.version=y.version),typeof y.updated_at=="string"&&(L.updated=y.updated_at),typeof y.downloads=="number"&&(L.downloads=y.downloads),typeof y.url=="string"&&(L.link=y.url),f.push({item:L,score:U})}f.sort((y,x)=>x.score-y.score||(x.item.downloads??0)-(y.item.downloads??0)||y.item.module.localeCompare(x.item.module));let m=f.length,h=m>o;return{q:r,modules:f.slice(0,o).map(y=>y.item),total:m,truncated:h,hint:Na}}function ed(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 Mo,Ea,La,Ca,Na,Ia,Fa,Da,$a,vr,No,kr,le,Be=C(()=>{"use strict";Mo=require("module");D();Ea=new Set(["tab","subflow","group","junction"]),La=100,Ca=30,Na="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.",Ia="https://catalogue.nodered.org/catalogue.json",Fa=3600*1e3,Da=["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(Ar,"isStructuralNodeType");a(Re,"requireNodeTypeName");a(Oa,"editDistance");a(Ma,"sharedPrefixLength");$a=new Set(["unknown","tab","subflow","group"]);a(ja,"nearestDistanceCutoff");a(Sr,"nearestNodeTypes");a(Ha,"trimTrailingSlash");a($o,"createPluginRequire");a(Ba,"isRuntimeSettingsAvailable");a(jo,"normalizeNodeSet");a(xr,"createStaticNodeTypesAdapter");vr=class{static{a(this,"HttpNodeTypesAdapter")}baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=Ha(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=jo(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()}},No={user:null,req:null},kr=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(No))}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=jo(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",...No}));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(Ho,"createNodeTypesAdapter");a(Tr,"flattenInstalledTypes");a(qa,"matchesQuery");a(Sn,"listNodeTypes");a(Ua,"extractHelpScriptBody");a(Io,"removeHtmlBlocks");a(Ga,"stripHelpText");a(Wa,"parseRegisterTypeFromHtml");a(Fo,"parseRegisterTypeAt");a(za,"splitHelpSections");a(Ut,"extractBalancedObject");a(Do,"matchStringProp");a(Oo,"matchNumberProp");a(Ja,"parseDefaultsBlock");a(Ka,"unescapeJsStringEscape");a(Va,"normalizeDefaultValue");a(An,"parseJsLiteral");a(br,"measureJsLiteralLength");a(Bo,"extractBalancedArray");a(Xa,"parseLooseValue");a(Ya,"findSetForType");a(Pr,"findCaseInsensitiveType");a(He,"getNodeTypeDetail");a(qo,"assertKnownNodeTypes");a(Uo,"assertKnownNodeType");le=null;a(Qa,"defaultCatalogueFetcher");a(Za,"loadCatalogue");a(xn,"searchPalette");a(ed,"paletteMatchScore")});function Ee(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 g(`${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 g(t.error,t.code,{statusCode:o,details:r})}function _r(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 td(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 Wo(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 ${td(c)}; node ids must be unique across all flows`,nodeId:l})}return o}function Go(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 rd(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]);for(let r of n)if(!nd.has(r)&&JSON.stringify(e[r])!==JSON.stringify(t[r]))return!0;return!1}function od(e,t){let n=Go(t);if(!e)return new Set(n.keys());let r=Go(e),o=new Set;for(let[s,i]of n){let d=r.get(s);(!d||rd(d,i))&&o.add(s)}return o}function zo(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(..._r(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{Re(c.type,"node.type")}catch(p){p instanceof g&&n.push({code:p.code,error:p.message,...typeof c.id=="string"?{nodeId:c.id}:{}})}"wires"in c&&ct(c.wires,n,typeof c.id=="string"?c.id:void 0)}return n}function ct(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 sd(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 He(t,n,{includeHelp:!1})).outputs}catch{return}}function id(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 ad(e){return Array.isArray(e)&&e.some(t=>typeof t=="string"&&t.length>0)}function dd(e,t){return t<=0?!1:!("wires"in e)||e.wires===void 0?!0:Array.isArray(e.wires)?!e.wires.some(ad):!1}async function Rr(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 sd(d,t),c=typeof d.type=="string"?d.type:"node",p=!i||i.has(d.id);if("wires"in d&&d.wires!==void 0){if(!ct(d.wires,r,d.id))continue;let u=d.wires;if(l!==void 0){let f=id(d,l,u);f?(f.cause="excess_ports",r.push(f)):p&&u.length<l&&o.push({code:"incomplete_wires",error:`${c} '${d.id}' declares ${l} outputs but wires has only ${u.length} port(s)`,nodeId:d.id,type:c})}for(let f of u)for(let m of f){let h=String(m);s.has(h)||r.push({code:"invalid_wires",error:`Wire target '${h}' on node '${d.id}' was not found in this flow (add the node in the same patch or use an existing id)`,nodeId:d.id,cause:"missing_target",targetId:h})}}p&&l!==void 0&&dd(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 Er(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 ld(e,t){return e.filter(n=>n.type===t).map(n=>n.id)}async function Lr(e,t,n){let r=[],o=[...t];for(let s of e){let i;try{i=Re(s.type,"node.type")}catch{continue}let d;try{d=await He(n,i,{includeHelp:!1})}catch{continue}let l=d.defaults;if(!l||typeof l!="object")continue;let c=new Set(d.requiredConfigNodeTypes??[]);for(let[p,u]of Object.entries(l)){if(!u||typeof u!="object"||Array.isArray(u))continue;let f=u.type;if(typeof f!="string"||!f)continue;let h=u.required===!0||c.has(f),y=s[p],x=ld(o,f);if(typeof y!="string"||y.length===0){h&&r.push({code:"missing_config_node",error:`Node '${s.id}' (${i}) has no ${f} on '${p}' yet \u2014 configure before deploy if needed`,nodeId:s.id,type:i,property:p,configType:f,candidates:x});continue}o.find(re=>re.id===y)?.type!==f&&r.push({code:"missing_config_node",error:`Node '${s.id}' property '${p}' does not resolve to a ${f} config yet`,nodeId:s.id,type:i,property:p,configType:f,candidates:x})}}return r}async function Jo(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(...Er(r,n));let d=t.baseFlow?od(t.baseFlow,e):void 0,l=await Rr(r,t.nodeTypes,{...d?{softWarningNodeIds:d}:{}});if(s.push(...l.errors),i.push(...l.warnings),t.nodeTypes){let c=await Lr(r,o,t.nodeTypes);d?i.push(...c.filter(p=>!p.nodeId||d.has(p.nodeId))):i.push(...c)}return{errors:s,warnings:i}}var nd,Cr=C(()=>{"use strict";D();Be();a(Ee,"throwValidationIssues");a(_r,"collectDuplicateNodeIdIssues");a(td,"describeLiveNode");a(Wo,"collectCrossFlowDuplicateIssues");nd=new Set(["x","y","w","h","g","id","z"]);a(Go,"collectNodesMap");a(rd,"nodeContentChanged");a(od,"changedNodeIds");a(zo,"collectFullFlowShapeIssues");a(ct,"assertValidWiresShape");a(sd,"resolveNodeOutputCount");a(id,"wireCountIssue");a(ad,"portHasTargets");a(dd,"nodeIsUnwired");a(Rr,"collectWireValidation");a(Er,"collectZMismatchIssues");a(ld,"configCandidates");a(Lr,"collectConfigWarnings");a(Jo,"validateFlowTab")});function Le(e,t="node"){return typeof e=="string"&&e.trim()?e.trim():t}function Tn(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 Vo(e,t){e[Ko]=t}function ud(e){if(!e)return;let t=e[Ko];return t instanceof Map?t:void 0}function fd(e){return typeof e!="string"||!e.trim()?new Set:new Set(e.split(",").map(t=>t.trim()).filter(Boolean))}function md(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 K(e){let t=fd(e.include);return md(e.include_payload)&&(t.add("payload"),t.add("baseFlow")),t}function N(e,t){let n={req:e};t!==void 0&&(n.httpAdminRoot=t);let r=ud(e);return r&&(n.liveFlowLabels=r),n}function pt(e){return typeof e.name=="string"&&e.name.trim()?e.name.trim():typeof e.label=="string"&&e.label.trim()?e.label.trim():""}function Gt(e){return`"${e.replace(/"/g,"'")}"`}function ut(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 gd(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 hd(e,t){let n=pt(e),r=pt(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 Nr(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&&gd(d,i)&&o.push({before:d,after:i})}return{added:n,removed:r,modified:o}}function Pn(e){return e===1?"node":"nodes"}function yd(e){return e.length===1&&e[0]?`adds 1 ${Le(e[0].type)}`:`adds ${e.length} ${Pn(e.length)}`}function wd(e){return`deletes ${e} ${Pn(e)}`}function bd(e){return`edits ${e} ${Pn(e)}`}function vd(e){let t=[];e.removed>0&&t.push(wd(e.removed));let n=e.added??[],r=e.addedCount??n.length;r>0&&t.push(n.length===r?yd(n):`adds ${r} ${Pn(r)}`),(e.modified??0)>0&&t.push(bd(e.modified??0));let o=t.length?t.join(", "):"replaces nodes",s=e.flowLabel?.trim();return s?`Replace ${s}: ${o}`:`Replace flow: ${o}`}function kd(e){let{added:t,removed:n,modified:r}=e;if(t.length+n.length+r.length!==1)return;if(t[0]){let p=t[0],u=Le(p.type),f=pt(p);return f?`Add ${u} node ${Gt(f)}`:`Add ${u} node`}if(n[0]){let p=n[0],u=Le(p.type),f=pt(p);return f?`Remove ${u} node ${Gt(f)}`:`Remove ${u} node`}let s=r[0];if(!s)return;let i=Le(s.after.type,Le(s.before.type)),d=pt(s.before),l=pt(s.after);if(hd(s.before,s.after)&&d&&l)return`Rename ${i} node ${Gt(d)} \u2192 ${Gt(l)}`;let c=l||d;return c?`Edit ${i} ${Gt(c)}`:`Edit ${i}`}function Ir(e,t){return typeof e?.label=="string"&&e.label.trim()?e.label.trim():typeof t?.label=="string"&&t.label.trim()?t.label.trim():""}function _n(e,t){if(typeof e.flowLabel=="string"&&e.flowLabel.trim())return e.flowLabel.trim();let n=Ir(e.payload?.flow,e.baseFlow);if(n)return n;let r=e.payload?.flowId;return r&&t?.has(r)?t.get(r)??"":""}function Xo(e,t){let n=t.trim(),r=e.trim();return!n||!r||r.includes(n)?r:`${r} in ${n}`}function Fr(e,t){let n=Nr(ut(e??{}),ut(t)),r=n.added.length+n.removed.length+n.modified.length,o=Ir(t,e);if(r===0)return o?`Update ${o}`:"Update flow";if(n.removed.length>0)return vd({...o?{flowLabel:o}:{},removed:n.removed.length,added:n.added,modified:n.modified.length});if(r>pd)return o?`${r} changes in ${o}`:`${r} changes`;let s=kd(n)??(o?`Update ${o}`:"Update flow");return Xo(s,o)}function Ad(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 Dr(e,t){return`${e} \xB7 ${xd(t)}`}function Sd(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 xd(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=Sd(-n.getTimezoneOffset());return`${r} ${s} ${i}:${d} ${l}`}function Rn(e){if(typeof e!="string")return;let t=e.replace(/\0/g,"").replace(/[\u0001-\u001F\u007F]/g," ").replace(/\s+/g," ").trim().slice(0,cd).trim();return t.length>0?t:void 0}function Td(e,t){let n=z(t);return`${n==="/"?"/mcp":`${n}/mcp`}#proposal=${encodeURIComponent(e)}`}function Or(e,t={}){let n=_n(e,t.liveFlowLabels),r=Rn(e.title),o=Fr(e.baseFlow,n&&!Ir(e.payload.flow,e.baseFlow)?{...e.payload.flow,label:n}:e.payload.flow),s=Nr(ut(e.baseFlow??{}),ut(e.payload.flow)),i=Ad(r,s.removed.length)?o:r??o;return Xo(i,n)}function Pd(e){return typeof e.storedTitle=="string"&&e.storedTitle?e.storedTitle:Rn(e.title)}function V(e,t={}){let n=Or(e,t),r=_n(e,t.liveFlowLabels),o=Pd(e)??n,s=Td(e.id,t.httpAdminRoot),i=t.req?Ht(t.req,s):s;return{...e,title:n,storedTitle:o,flowLabel:r,displayName:Dr(n,e.createdAt),reviewUrl:i,reviewPath:s}}function _d(e){let t=Nr(ut(e.baseFlow??{}),ut(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 Rd(e){let t={kind:e.kind,linkId:e.linkId};return typeof e.label=="string"&&e.label.length>0&&(t.label=e.label),t}function Z(e,t={}){let n=V(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:Rd(n.createdBy),changeSummary:_d(e)};return r.has("payload")&&(o.payload=n.payload),r.has("baseFlow")&&n.baseFlow!==void 0&&(o.baseFlow=n.baseFlow),n.warnings&&n.warnings.length>0&&(o.warnings=n.warnings),n.baseRevStale!==void 0&&(o.baseRevStale=n.baseRevStale),n.decidedBy!==void 0&&(o.decidedBy=n.decidedBy),n.reason!==void 0&&(o.reason=n.reason),n.resultRev!==void 0&&(o.resultRev=n.resultRev),o}function Yo(e,t={}){return e.map(n=>Z(n,t))}var cd,pd,Ko,ce=C(()=>{"use strict";$e();Se();cd=80,pd=1;a(Le,"nodeTypeText");Ko=Symbol.for("ai-access.liveFlowLabels");a(Tn,"liveFlowLabelsFromFlows");a(Vo,"setRequestFlowLabels");a(ud,"getRequestFlowLabels");a(fd,"parseIncludeQuery");a(md,"isTruthyFlag");a(K,"resolveIncludeFromQuery");a(N,"presentOptionsFor");a(pt,"nodeLabel");a(Gt,"quoteName");a(ut,"collectNodes");a(gd,"nodeContentChanged");a(hd,"isRenameOnly");a(Nr,"classifyFlowChanges");a(Pn,"pluralNodes");a(yd,"describeAddedClause");a(wd,"describeRemovedClause");a(bd,"describeModifiedClause");a(vd,"buildReplaceFlowTitle");a(kd,"describeOneChange");a(Ir,"flowLabelHint");a(_n,"resolveFlowLabel");a(Xo,"ensureTitleIncludesFlowName");a(Fr,"autoGenerateProposalTitle");a(Ad,"shouldRefreshDisplayTitle");a(Dr,"buildProposalDisplayName");a(Sd,"formatUtcOffset");a(xd,"formatProposalSubmittedAt");a(Rn,"sanitizeProposalTitle");a(Td,"buildProposalReviewPath");a(Or,"resolveProposalTitle");a(Pd,"storedTitleOf");a(V,"presentProposal");a(_d,"buildChangeSummary");a(Rd,"presentCreatedByForApi");a(Z,"presentProposalForApi");a(Yo,"presentProposalsForApi")});function Qo(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 Ld(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let o of n)Ed.has(o)||JSON.stringify(e[o])!==JSON.stringify(t[o])&&r.push(o);return r.sort()}function Zo(e){let t=Qo(e.baseFlow??{}),n=Qo(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=Ld(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 Cd(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(p=>p!=="*")),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 O(e,t,n={}){let r=0;for(let o of e.list())if(o.status==="pending"&&!(n.flowId&&o.payload.flowId!==n.flowId)&&o.expectedRev!==t)try{e.markClosed(o.id,"expired",Nd),r+=1}catch{}return r}function es(e,t,n,r){let o=`Superseded by approved proposal ${n} (resultRev ${r}). Re-read the flow and resubmit if still needed.`,s=0;for(let i of e.list())if(!(i.id===n||i.status!=="pending"||i.payload.flowId!==t))try{e.markClosed(i.id,"superseded",o),s+=1}catch{}return s}function ye(e,t,n){let r=Zo(t),o=[];for(let s of e.list()){if(s.id===t.id||s.status!=="pending"||s.payload.flowId!==t.payload.flowId)continue;let i=Or(s),d=n?s.createdBy.linkId===n:s.createdBy.linkId===t.createdBy.linkId,l=Cd(r,Zo(s)),c=s.expectedRev===t.expectedRev,p=l.nodeIds.length>0;if(!c&&!p)continue;let u={id:s.id,title:i,displayName:Dr(i,s.createdAt),mine:d};l.nodeIds.length>0&&(u.overlappingNodeIds=l.nodeIds),l.fields.length>0&&(u.overlappingFields=l.fields),o.push(u)}return{conflictingProposals:o}}function we(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 H(e){return e>0?{autoClosedCount:e}:{}}var Ed,Nd,Ce=C(()=>{"use strict";ce();Ed=new Set(["x","y","w","h","g","id","z"]);a(Qo,"collectNodes");a(Ld,"changedFieldKeys");a(Zo,"computeProposalTouches");a(Cd,"overlapBetween");Nd="Proposal expired: the live flow revision moved past expectedRev. Re-read the flow and resubmit.";a(O,"expireStalePendingProposals");a(es,"supersedePendingOnFlow");a(ye,"findConflictingProposals");a(we,"conflictWarningFields");a(H,"autoClosedFields")});var ds={};uo(ds,{applyFlowNodeOps:()=>qe,applyReplaceTextToValue:()=>as,buildPatchedFlowTab:()=>Wt,collectSetProps:()=>Mr,decodeBase64UrlJson:()=>zt,findTextOccurrences:()=>is,interpretPatch:()=>Jt});function ss(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function Id(e){if(typeof e!="object"||e===null||Array.isArray(e))throw new g(`add op needs node as an object (got ${ss(e)})`,"invalid_node")}async function Wt(e,t,n){return qe(e,t,n.map(r=>({op:"set",nodeId:r.nodeId,set:r.set})))}function is(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 as(e,t,n,r,o){if(os.has(t))throw new g(`Cannot change protected node property '${t}' via replace_text`,"protected_property");if(typeof n!="string"||n.length===0)throw new g("find must be a non-empty string","missing_find");if(typeof r!="string")throw new g("replace must be a string","invalid_replace");if(e===void 0)throw new g(`Field '${t}' was not found on the node`,"field_not_found");if(typeof e!="string")throw new g(`Field '${t}' is not a string (got ${e===null?"null":typeof e})`,"field_not_string");let s=is(e,n);if(s.length===0)throw new g(`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 g(`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 g(`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 g(`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 Fd(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 ${ss(r)})`});continue}let o=r;(!o.id||typeof o.id!="string")&&t.push({code:"missing_node_id",error:"add op needs node.id"});try{Re(o.type,"node.type")}catch(s){s instanceof g&&t.push({code:s.code,error:s.message,nodeId:o.id})}"wires"in o&&ct(o.wires,t,typeof o.id=="string"?o.id:void 0)}else n.op==="set"&&typeof n.set=="object"&&"wires"in n.set&&ct(n.set.wires,t,n.nodeId);return t}function Dd(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 qe(e,t,n,r={}){if(!t)throw new g("flow id is required","missing_flow_id");if(!n.length)throw new g("At least one node edit is required","missing_edits");let o=Fd(n);o.push(..._r([n.flatMap(h=>h.op==="add"?[h.node]:[])],"the batch (add ops)")),Ee(o);let s=await e.getFlow(t);if(r.expectedRev!==void 0&&s.rev!==r.expectedRev){let h=Array.isArray(s.flow.nodes)?[...s.flow.nodes]:[];return{expectedRev:s.rev,flow:{...s.flow,id:s.flow.id,type:s.flow.type??"tab",nodes:h}}}let i=Array.isArray(s.flow.nodes)?[...s.flow.nodes]:[],d=Array.isArray(s.flow.configs)?[...s.flow.configs]:[],l={...s.flow,id:s.flow.id,type:s.flow.type??"tab",nodes:i,configs:d},c=i.map(h=>({...h})),p=[];for(let h of n)try{h.op==="add"&&r.nodeTypes&&await Uo(r.nodeTypes,h.node.type),ns(c,t,h)}catch(y){if(y instanceof g)p.push({code:y.code,error:y.message,...y.statusCode!==400?{statusCode:y.statusCode}:{},...y.details??{}});else throw y}p.push(...Er(c,t));let u=Dd(n),f=await Rr(c,r.nodeTypes,{softWarningNodeIds:u});p.push(...f.errors),Ee(p);for(let h of n)ns(i,t,h);let m=[...f.warnings];if(r.nodeTypes){let h=await Lr(i,d,r.nodeTypes);m.push(...h.filter(y=>!y.nodeId||u.has(y.nodeId)))}return{expectedRev:s.rev,flow:l,...m.length>0?{warnings:m}:{}}}function ts(e,t,n){if(!t||typeof t!="string")throw new g("Each edit needs a node id","missing_node_id");let r=e.findIndex(o=>o.id===t);if(r<0||!e[r])throw new g(`Node '${t}' was not found in flow '${n}'`,"node_not_found",{statusCode:404});return r}function ns(e,t,n){if(n.op==="add"){let i=n.node;if(!i.id||typeof i.id!="string")throw new g("add op needs node.id","missing_node_id");if(Re(i.type,"node.type"),e.some(d=>d.id===i.id))throw new g(`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=ts(e,n.nodeId,t);e.splice(i,1);return}let r=ts(e,n.nodeId,t),o=e[r];if(!o)throw new g(`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 g(`Field '${n.field}' was not found on the node`,"field_not_found");let i=as(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(os.has(i))throw new g(`Cannot change protected node property '${i}' via GET propose`,"protected_property");if(i==="wires"){let l=[];ct(d,l,n.nodeId),Ee(l)}s[i]=d}e[r]=s}function rs(e){if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean"||typeof e=="bigint")return String(e)}function Mr(e){let t={},n=e.set;if(n&&typeof n=="object"&&!Array.isArray(n))for(let[r,o]of Object.entries(n)){let s=rs(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=rs(o);i!==void 0&&(t[s]=i)}}return t}function zt(e){let t;try{t=Buffer.from(e,"base64url").toString("utf8")}catch{throw new g("patch must be base64url-encoded JSON","invalid_patch")}try{return JSON.parse(t)}catch{throw new g("patch JSON is malformed","invalid_patch_json")}}function Od(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 $r(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 Md(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 g("occurrence must be a positive integer","invalid_occurrence");if(!Number.isFinite(t))throw new g("occurrence must be a positive integer","invalid_occurrence");return Math.trunc(t)}function $d(e){let t=e.node;if(t!==void 0)return Id(t),{op:"add",node:t};let n={...e};return delete n.op,{op:"add",node:n}}function jd(e){let t=$r(e);if(!t)throw new g("replace_text op needs node id","missing_node_id");let n=typeof e.field=="string"?e.field:"";if(!n)throw new g("replace_text op needs field","missing_field");if(typeof e.find!="string")throw new g("replace_text op needs find string","missing_find");if(typeof e.replace!="string")throw new g("replace_text op needs replace string","invalid_replace");let r=Md(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 Hd(e){let t=$r(e);if(!t)throw new g("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 Bd(e){let t=typeof e.op=="string"?e.op.toLowerCase():"set";if(t==="add")return $d(e);if(t==="remove"){let n=$r(e);if(!n)throw new g("remove op needs id","missing_node_id");return{op:"remove",nodeId:n}}if(t==="replace_text")return jd(e);if(t!=="set")throw new g("nodes[].op must be 'add', 'remove', 'set', or 'replace_text'","invalid_patch");return Hd(e)}function Jt(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new g("patch must be a JSON object","invalid_patch");let n=e;if(n.type==="replace_flow"||n.flow&&typeof n.flow=="object"){let r=Od(n,t);if(!r)throw new g("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 g("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 g("Each patch.nodes entry must be an object","invalid_patch");o.push(Bd(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 g("patch must include nodes[] edits or type/flow for replace_flow","invalid_patch")}var os,En=C(()=>{"use strict";D();Cr();Be();os=new Set(["id","type","z"]);a(ss,"describeNonObjectNode");a(Id,"assertAddNodeObject");a(Wt,"buildPatchedFlowTab");a(is,"findTextOccurrences");a(as,"applyReplaceTextToValue");a(Fd,"collectOpShapeIssues");a(Dd,"touchedNodeIdsFromOps");a(qe,"applyFlowNodeOps");a(ts,"requireNodeIndex");a(ns,"applyOneNodeOp");a(rs,"stringifyQueryValue");a(Mr,"collectSetProps");a(zt,"decodeBase64UrlJson");a(Od,"resolveReplaceFlowId");a($r,"resolveNodeId");a(Md,"parseOccurrence");a($d,"parseAddOp");a(jd,"parseReplaceTextOp");a(Hd,"parseSetOp");a(Bd,"parseNodeOp");a(Jt,"interpretPatch")});function ft(e,t){return e.createdBy.linkId===t.id}function jr(e,t){return!t||t.length===0||t.includes(e)}function qd(e){return typeof e=="string"&&cs.has(e)}function gt(e,t){let n=Ud[e],r=e!=="status"&&Array.isArray(t);return new g(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 Ln(e,t){if(Array.isArray(t))throw gt(e,t);return t}function Gd(e){return typeof e=="number"?Number.isInteger(e)?e:null:typeof e=="string"&&/^-?\d+$/.test(e.trim())?Number(e.trim()):null}function Hr(e){return e==null||e===""}function Wd(e){if(!Hr(e)){if(e==="true"||e==="1"||e===!0)return!0;if(e==="false"||e==="0"||e===!1)return!1;throw gt("mine",e)}}function Br(e){if(Hr(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(!qd(o))throw gt("status",e);n.includes(o)||n.push(o)}}return n.length>0?n:void 0}function ls(e,t,n,r){if(Hr(t))return;let o=Gd(t);if(o===null||o<n||o>r)throw gt(e,t);return o}function Cn(e){let t={},n=Wd(Ln("mine",e.mine));n!==void 0&&(t.mine=n);let r=Br(e.status);r&&(t.status=r);let o=Ln("flowId",e.flowId??e.flow_id??e.flow);typeof o=="string"&&o.length>0&&(t.flowId=o);let s=ls("limit",Ln("limit",e.limit),1,mt);s!==void 0&&(t.limit=s);let i=ls("offset",Ln("offset",e.offset),0,Number.MAX_SAFE_INTEGER);return i!==void 0&&(t.offset=i),t}function Ue(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 zd(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),mt);return{items:e.slice(r,r+o),total:n,limit:o,offset:r}}function ht(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||!ft(c,t)).filter(c=>jr(c.status,r.status)).map(c=>({...V(c,n),mine:ft(c,t)})),l=zd(d,r);return{proposals:l.items,total:l.total,limit:l.limit,offset:l.offset}}function Te(e,t,n,r={}){if(!n)throw new g("Missing proposal id","missing_proposal_id");let o=e.get(n);return{...V(o,r),mine:ft(o,t)}}function Jd(e){let t={kind:"access-link",linkId:e.id};return typeof e.label=="string"&&e.label&&(t.label=e.label),t}function yt(e,t,n,r,o,s={}){if(!r)throw new g("Missing proposal id","missing_proposal_id");let i;try{i=e.get(r)}catch(f){throw f instanceof J,f}if(!ft(i,n))throw new at(r);if(i.status!=="pending")throw new G(r,i.status);let d=typeof o=="string"&&o.trim()?o.replace(/\0/g,"").trim().slice(0,2e3):"Withdrawn by access link",l=Jd(n),c=e.markWithdrawn(r,l,d),p=V(c,s),u=p.flowLabel||void 0;return t.append("proposal.withdrawn",l,{proposalId:c.id,flowId:c.payload.flowId,title:p.title,displayName:p.displayName,reason:d,...u?{flowLabel:u}:{}}),{...p,mine:!0}}function Kd(e,t,n,r={}){let o;try{o=e.get(t.id)}catch{return{}}if(r.liveRev){O(e,r.liveRev,{flowId:o.payload.flowId});try{o=e.get(t.id)}catch{return{}}}return o.status!=="pending"?{}:we(ye(e,o,n))}function ee(e,t,n,r={},o={}){let s="mine"in n?n:{...V(n,r),mine:ft(n,t)},i=0;if(o.liveRev)try{let c=e.get(s.id).payload.flowId;i=O(e,o.liveRev,{flowId:c});let p=e.get(s.id);s={...V(p,r),mine:ft(p,t)}}catch{}let d=Z(s,r),l={...d,mine:s.mine,...H(i)};return o.includeConflicts!==!1&&d.status==="pending"&&Object.assign(l,Kd(e,d,t.id)),l}async function te(e){let t=await e.getFlows();return{rev:t.rev,flows:t.flows}}async function pe(e,t){if(!t)throw new g("Missing flow id","missing_flow_id");let n=await e.getFlow(t);return{rev:n.rev,flow:lt(t,n.flow)}}function Vd(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 ps(e,t){if("nodes"in e&&e.nodes!==void 0&&!Array.isArray(e.nodes))throw new g("payload.nodes must be an array of node ops","invalid_nodes");if(t!==void 0&&!Array.isArray(t))throw new g("nodes must be an array of node ops","invalid_nodes")}async function Xd(e,t,n,r,o){let{applyFlowNodeOps:s,interpretPatch:i}=await Promise.resolve().then(()=>(En(),ds));ps(n,r);let d=Array.isArray(n.nodes)?n.nodes:Array.isArray(r)?r:null;if(!d)throw new g("payload.flow is required","missing_flow");let l=n.flowId,c=typeof l=="string"?l:"";if(!c)throw new g("payload.flowId is required","missing_flow_id");let p=i({nodes:d,flowId:c},c);if(p.kind!=="nodes")throw new g("payload.nodes must be node ops","invalid_patch");let u=await s(e,p.flowId,p.ops,{expectedRev:t,...o?{nodeTypes:o}:{}});if(u.expectedRev!==t)throw new F(t,u.expectedRev,"submit_proposal");return{flowId:p.flowId,flow:u.flow}}async function Yd(e,t,n,r,o){let s=n.flowId,i=n.flow,d=!i||typeof i!="object"||Array.isArray(i);if(d&&ps(n,r),d&&(Array.isArray(n.nodes)||Array.isArray(r))){let l=await Xd(e,t,n,r,o);s=l.flowId,i=l.flow}if(typeof s!="string"||s.length===0)throw new g("payload.flowId is required","missing_flow_id");if(!i||typeof i!="object"||Array.isArray(i))throw new g("payload.flow is required","missing_flow");return{flowId:s,flow:i}}function Qd(e,t){try{return{safeExpectedRev:xe(e.slice(0,200),"expectedRev"),safeFlowId:xe(t.slice(0,200),"flowId")}}catch{throw new g("expectedRev and payload.flowId must be safe path segments","invalid_path_segment")}}async function Ne(e,t,n,r,o,s={},i){let d=r.type,l=Vd(r),c=r.payload;if(d!=="replace_flow")throw new g("type must be 'replace_flow'","invalid_proposal_type");if(!l)throw new g("expectedRev is required (top-level field next to type and payload)","missing_expected_rev");if(!c||typeof c!="object"||Array.isArray(c))throw new g("payload must be an object","invalid_payload");let u=await Yd(o,l,c,r.nodes,i),{safeExpectedRev:f,safeFlowId:m}=Qd(l,u.flowId),h=u.flow,y=Buffer.byteLength(JSON.stringify(c),"utf8");if(y>E)throw new M("proposal payload",y,E);Ee(zo(h,m));let k=it(h),re=await o.getFlows();if(re.rev!==f)throw new F(f,re.rev,"submit_proposal");Ee(Wo(k,m,re.flows));let U=await o.getFlow(m);if(U.rev!==f)throw new F(f,U.rev,"submit_proposal");let L=it(U.flow);i&&await qo(i,k);let{errors:Nt,warnings:et}=await Jo(k,{flowId:m,baseFlow:L,...i?{nodeTypes:i}:{}});Ee(Nt);let W={kind:"access-link",linkId:n.id};n.label!==void 0&&(W.label=n.label);let oe=Rn(r.title)??Fr(L,k),co=e.create({type:"replace_flow",expectedRev:f,payload:{flowId:m,flow:k},baseFlow:L,title:oe,createdBy:W,...et.length>0?{warnings:et}:{}}),sn=V(co,s),po=sn.flowLabel||void 0;return t.append("proposal.created",W,{proposalId:co.id,flowId:m,expectedRev:f,title:sn.title,displayName:sn.displayName,...po?{flowLabel:po}:{},...et.length>0?{warningCount:et.length}:{}}),sn}function P(e,t="rest"){return Kt(e,t)}var mt,cs,Ud,g,D=C(()=>{"use strict";de();kn();dt();wn();$();be();Cr();Be();Ce();ce();a(ft,"isMineProposal");mt=200,cs=new Set(["pending","approved","rejected","failed","withdrawn","expired","superseded"]);a(jr,"matchesStatusFilter");a(qd,"isProposalStatus");Ud={status:[...cs].join(", "),limit:`integer 1\u2013${mt}`,offset:"integer \u2265 0",mine:"true, false, 1, 0",flowId:"one flow tab id"};a(gt,"invalidFilterError");a(Ln,"singleQueryValue");a(Gd,"parseIntegerFilter");a(Hr,"isUnset");a(Wd,"parseMineFilter");a(Br,"parseStatusFilter");a(ls,"parseRangeFilter");a(Cn,"parseListProposalsQuery");a(Ue,"listPageFields");a(zd,"applyListPagination");a(ht,"listAccessibleProposals");a(Te,"getAccessibleProposal");a(Jd,"accessLinkActor");a(yt,"withdrawMyProposal");a(Kd,"conflictsForProposal");a(ee,"presentAccessibleProposalForApi");g=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(te,"listFlows");a(pe,"getFlow");a(Vd,"resolveExpectedRev");a(ps,"assertNodesArrayOrAbsent");a(Xd,"materializePayloadFromNodes");a(Yd,"resolveReplaceFlowPayload");a(Qd,"assertSafeRevAndFlowId");a(Ne,"submitReplaceFlowProposal");a(P,"aiOpsErrorToHttp")});function qr(e){return e==="get_link"?86400:2592e3}function Ur(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 Ge,Vt=C(()=>{"use strict";a(qr,"maxTtlSecondsForKind");a(Ur,"ttlCapErrorMessage");Ge="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 b(e,t,n,r,o=vt,s="error"){return{code:e,status:t,meaning:n,hint:r,channels:o,kind:s}}function Xt(e){return el.get(e)?.hint}function us(e){return e.replace(/\\/g,"\\\\").replace(/\|/g,"\\|")}function fs(){let e=a(s=>`| \`${s.code}\` | ${s.status} | ${us(s.meaning)} | ${us(s.hint)} | ${s.channels.join(", ")} |`,"row"),t=["| Code | HTTP | Meaning | Next step | Channels |","| --- | --- | --- | --- | --- |"],n=Nn.filter(s=>s.kind==="error"),r=Nn.filter(s=>s.kind==="warning"),o=Nn.filter(s=>s.kind==="status");return[...t,...n.map(e),"","**Warnings** (in `warnings[]` on a successful propose; never blocking):","",...t,...r.map(e),"","**Auto-closed proposal statuses.** Pending proposals are closed automatically: `expired` when the live flow rev moves past their `expectedRev`, `superseded` when another proposal on the same flow is approved. Submit, get, and list responses include `autoClosedCount` when that request auto-closed proposals (omitted when zero).","",...t,...o.map(e)].join(`
|
|
7
|
-
`)}var vt,wt,Gr,Zd,bt,Nn,el,In=C(()=>{"use strict";Vt();vt=["GET","REST","MCP"],wt=["GET"],Gr=["REST","MCP"],Zd=["GET","REST"],bt=["admin"];a(b,"e");Nn=[b("access_link_missing",401,"No Bearer / path token","Ask the user for a new access link of the right kind"),b("bearer_scheme_required",401,"Auth used Basic (or non-Bearer)","Send `Authorization: Bearer <token>`",Gr),b("token_expired",401,"Known token past `expiresAt` (body includes `expiredAt` and `accessLinksUrl`)","Ask the user to create a new link on the `/mcp` page \u2192 Access links (`accessLinksUrl`)"),b("access_link_invalid",401,"Unknown, revoked, or cleared after restart","Ask the user for a new access link"),b("wrong_token_kind",403,"Token kind does not match this surface","Mint the matching kind on `/mcp` \u2192 Access links; show `tellUser` to the human"),b("ai_features_disabled",403,"Plugin AI features off","Owner enables AI Access / `adminAuth`"),b("auth_required",401,"Admin session required","Sign in to the Node-RED editor",bt),b("insufficient_permissions",403,"Admin lacks required scope","Need `*` for link mint / approve",bt),b("invalid_link_kind",400,"Unknown `kind` on create","Use `get_link`, `rest`, or `mcp`",bt),b("invalid_ttl",400,"`ttlSeconds` is zero, negative, not a number, or over the cap for the kind",Ge,bt),b("access_link_limit",409,"Too many active links","Revoke an existing link first",bt),b("access_link_not_found",404,"Unknown link id on revoke","List links and use an existing id",bt),b("link_create_not_supported",405,"Attempted mint under a GET link","Admin mints via `POST /mcp/api/v1/links` only",wt),b("rate_limited",429,"Per-IP or per-token limit","Wait `retryAfter` / `Retry-After`"),b("payload_too_large",413,"Body / proposal over ~1.5MB","Shrink content"),b("invalid_json",400,"Malformed JSON body","Fix Content-Type / body",Gr),b("unsupported_media_type",415,"POST body is not `application/json`","Send `Content-Type: application/json`",Gr),b("url_too_long",414,"GET URL over soft cap (~8KB)","Use chunked drafts or REST/MCP",wt),b("not_found",404,"Unknown path or resource","Re-read docs / discovery"),b("missing_query",400,"Required search `q` missing","Pass `?q=`",Zd),b("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"),b("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"]),b("invalid_diff_format",400,"Unknown `format` on proposal diff","Use `format=summary` (default) or `format=full`"),b("confirm_required",400,"GET withdraw without `confirm=1`","Repeat the request with `confirm=1`",wt),b("missing_expected_rev",400,"Proposal submitted without `expectedRev` / `rev`","Read the flow and pass its `rev`"),b("version_mismatch",409,"Stale `rev` / `expectedRev`","Re-read flows and resubmit"),b("proposal_not_found",404,"Unknown proposal id","List proposals and use an existing id"),b("proposal_not_pending",409,"Action needs a pending proposal","Only pending can withdraw / decide"),b("proposal_not_owned",403,"Withdraw of another token\u2019s proposal","Only the creating token can withdraw"),b("validation_failed",400,"Several validation errors at once (message `N validation errors`)","Fix every entry in errors[] and resubmit."),b("duplicate_node_id",400,"The same node id appears more than once in a full tab or batch","Give every node a unique id"),b("missing_node_id",400,"Node / op without an id","Include a non-empty string id"),b("missing_node_type",400,"Node without a `type`","Set `type` to an installed palette type name"),b("invalid_node_type",400,"`type` is not a string","Use the palette type name exactly"),b("invalid_nodes",400,"`flow.nodes` / `flow.configs` not an array","Send arrays (use `[]` to clear)"),b("invalid_node",400,"Node entry is not an object","Send `{ id, type, \u2026 }` objects"),b("invalid_wires",400,"Bad wires shape, missing target, or more ports than outputs","Wire only to existing node ids / ports"),b("z_mismatch",400,"Node `z` differs from the flow id","Set `z` to the tab id or omit it"),b("node_already_exists",400,"`add` op for an id already in the flow","Use `set` or a new id"),b("protected_property",400,"Tried to change id / type / z","Omit id, type, and z from edits"),b("node_not_found",404,"Unknown node (single error only; several errors \u2192 `validation_failed`)","Re-read the flow"),b("field_not_found",404,"Unknown field on a node","Re-read the node"),b("text_not_found",400,"`replace_text` find had no match","Re-read field; find is exact"),b("ambiguous_match",409,"`replace_text` matched more than once","Pass `occurrence` (1-based) or unique find"),b("unknown_node_type",400,"Proposal uses a type that is not installed","List node-types / palette search; ask user to install"),b("node_type_not_found",404,"Unknown type on node-types detail","List types or search palette"),b("chunk_conflict",409,"Same draft seq re-sent with different data","Re-send identical data or start a new draft",wt),b("missing_chunks",400,"Draft submit with gaps in seq","Upload every seq from 0 through max",wt),b("draft_hash_mismatch",400,"Draft sha256 does not match assembled bytes","Send the hex sha256 of the assembled UTF-8",wt),b("missing_config_node",201,"Warning: node has no (or an unresolved) config node yet","Configure before deploy if needed; see `candidates`",vt,"warning"),b("unwired_node",201,"Warning: node has outputs but no wires","Wire it, or ignore while scaffolding",vt,"warning"),b("incomplete_wires",201,"Warning: fewer wire ports than outputs","Add ports if outputs should connect",vt,"warning"),b("expired",200,"Status: pending proposal auto-closed because the live flow rev moved past `expectedRev`","Re-read the flow and submit a new proposal",vt,"status"),b("superseded",200,"Status: pending proposal auto-closed because another proposal on the same flow was approved","Re-read the flow and submit a new proposal if still needed",vt,"status"),b("upstream_error",502,"Node-RED runtime/flows error","Retry; check Node-RED logs"),b("internal_error",500,"Unexpected server error","Retry; check Node-RED logs")],el=new Map(Nn.map(e=>[e.code,e]));a(Xt,"registryHint");a(us,"cell");a(fs,"renderGetLinkErrorTable")});function X(e,t,n={}){return Wr(e,t,n)}function tl(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 nl(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 Ie(e,t,n,r){return e==="mcp"?t:e==="rest"?n:r}function Wr(e,t,n={}){switch(e){case"version_mismatch":return Ie(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 Ie(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 Ie(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 Ie(t,"Call list_proposals and use an id from that response.","List proposals and use an id from that response.","List proposals and use an id from that response.");case"proposal_not_owned":return"Only the access link that created a pending proposal can withdraw it.";case"proposal_not_pending":return"Only pending proposals can be withdrawn or decided.";case"confirm_required":return"Add confirm=1 to the withdraw URL (required so link prefetchers cannot withdraw).";case"invalid_wires":return tl(t,n.cause);case"node_type_not_found":case"unknown_node_type":return nl(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 Ie(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 Ie(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 Ie(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 Ie(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 Kt(e,t,n={}){let r=n.canonicalizeMessage!==!1,o=null;if(e instanceof g?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,...e.details??{}}}:e instanceof F?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,expectedRev:e.expectedRev,currentRev:e.currentRev}}:e instanceof I?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,flowId:e.flowId}}:e instanceof J||e instanceof at||e instanceof G||e instanceof M?o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}:(e instanceof he||e instanceof ae)&&(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=ve[s];i&&(o.body.error=i)}if(typeof o.body.hint!="string"||!o.body.hint){let i=Wr(s,t,{cause:o.body.cause})??Xt(s);i&&(o.body.hint=i)}return o}function ms(e,t,n={}){let o={error:n.error??ve[t]??"Request failed",code:t,...n.details??{}},s=Wr(t,e,{cause:o.cause})??Xt(t);return s&&(o.hint=s),{statusCode:n.statusCode??400,body:o}}var ve,be=C(()=>{"use strict";de();dt();$();D();In();ve={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(X,"channelErrorHint");a(tl,"invalidWiresHint");a(nl,"nodeTypeHint");a(Ie,"pickChannel");a(Wr,"hintFor");a(Kt,"mapErrorForChannel");a(ms,"buildChannelError")});var Kp={};uo(Kp,{ACCESS_LINK_INVALID_MESSAGE:()=>Oe,ACCESS_LINK_MISSING_MESSAGE:()=>Q,AccessLinkStore:()=>Qe,EDITOR_SIDEBAR_ASSET_VERSION:()=>xt,MAX_PROPOSAL_JSON_BYTES:()=>E,SettingsStore:()=>nt,areAiFeaturesEnabled:()=>ie,bindSettingsStore:()=>fn,createPluginServices:()=>cr,default:()=>Jp,getStorageDir:()=>en,isAdminAuthEnabled:()=>Y,packageVersion:()=>St,registerRoutes:()=>Wi,renderEditorSidebarJs:()=>Fn,renderEditorSidebarLoaderHtml:()=>hs,requireAccessLink:()=>st,resolveAiAccessSettings:()=>se});module.exports=Qi(Kp);Me();Se();function To(e){let t=z(e);return`auth-tokens${(t==="/"?"":t).replace(/\//g,"-")}`}a(To,"editorAuthTokenKey");Se();be();function rl(e){if(!e)return!1;let t=e.split(";")[0]?.trim().toLowerCase()??"";return t==="application/json"||t.endsWith("+json")}a(rl,"isJsonContentType");function ol(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(ol,"hasRequestBody");function kt(e="rest"){return(t,n,r)=>{let o=t.headers["content-type"];if(rl(o)){r();return}if(!o&&!ol(t)){r();return}let s=ms(e,"unsupported_media_type",{statusCode:415});n.status(s.statusCode).json(s.body)}}a(kt,"requireJsonContentType");Se();function sl(e){let t=me(e.settings.httpAdminRoot),n=Pe(e.settings.httpAdminRoot),r="0.1.0",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 /mcp Access links page (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(sl,"buildDiscoveryIndex");function zr(e){return(t,n)=>{n.status(200).json(sl(e))}}a(zr,"discoveryIndexHandler");function gs(){return(e,t)=>{t.status(404).json({error:"Not found",code:"not_found"})}}a(gs,"mcpApiNotFoundHandler");var At;function St(){return At||(At="0.1.0",At)}a(St,"packageVersion");var xt=St();function il(){return`#red-ui-sidebar-ai-access {
|
|
3
|
+
`,o=Buffer.from(r,"utf8");if(o.byteLength>t)throw new M(n.kind,o.byteLength,t);return{bytes:o}}function yo(e,t){let n=(0,Ot.resolve)(t),r=(0,Ot.resolve)(e);if(r!==n&&!r.startsWith(n+Ot.sep))throw new Error("Path escapes .ai-access storage root");return r}function an(e,t,n){let r=yo(t,e),o=`${r}.${process.pid}.tmp`;(0,ke.writeFileSync)(o,n.bytes,{mode:sn}),(0,ke.renameSync)(o,r),(0,ke.chmodSync)(r,sn)}function wo(e,t,n){let r=yo(t,e);(0,ke.appendFileSync)(r,n.bytes,{mode:sn});try{(0,ke.chmodSync)(r,sn)}catch{}}var ke,Ot,pr,sn,E,ho,M,$=C(()=>{"use strict";ke=require("fs"),Ot=require("path"),pr=448,sn=384,E=15e5,ho=8192,M=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(tt,"encodeTrustedJson");a(yo,"assertUnderAiAccess");a(an,"writeTrustedAtomic");a(wo,"appendTrusted")});function fa(e){return e==="nodes"||e==="flows"||e==="full"}function dn(e){if(!e||typeof e!="object"||Array.isArray(e))return{};let t=e,n={};return typeof t.enabled=="boolean"&&(n.enabled=t.enabled),typeof t.allowWithoutAdminAuth=="boolean"&&(n.allowWithoutAdminAuth=t.allowWithoutAdminAuth),typeof t.defaultLinkTtlSeconds=="number"&&Number.isFinite(t.defaultLinkTtlSeconds)&&t.defaultLinkTtlSeconds>0&&(n.defaultLinkTtlSeconds=Math.floor(t.defaultLinkTtlSeconds)),fa(t.defaultDeploymentType)&&(n.defaultDeploymentType=t.defaultDeploymentType),typeof t.maxActiveLinks=="number"&&Number.isFinite(t.maxActiveLinks)&&t.maxActiveLinks>0&&(n.maxActiveLinks=Math.floor(t.maxActiveLinks)),n}function ma(e){if(!(0,cn.existsSync)(e))return{};try{let t=(0,cn.readFileSync)(e,"utf8");return dn(JSON.parse(t))}catch{return{}}}function ln(e,t){let n={enabled:"default",allowWithoutAdminAuth:"default",defaultLinkTtlSeconds:"default",defaultDeploymentType:"default",maxActiveLinks:"default"},r=a((o,s)=>t[o]!==void 0?(n[o]="disk",t[o]):e[o]!==void 0?(n[o]="settings.js",e[o]):(n[o]="default",s),"pick");return{enabled:r("enabled",Dt.enabled),allowWithoutAdminAuth:r("allowWithoutAdminAuth",Dt.allowWithoutAdminAuth),defaultLinkTtlSeconds:r("defaultLinkTtlSeconds",Dt.defaultLinkTtlSeconds),defaultDeploymentType:r("defaultDeploymentType",Dt.defaultDeploymentType),maxActiveLinks:r("maxActiveLinks",Dt.maxActiveLinks),sources:n}}function ur(e){return dn(e??{})}var cn,bo,ua,Dt,nt,pn=C(()=>{"use strict";cn=require("fs"),bo=require("path");$();ua=16384,Dt={enabled:!0,allowWithoutAdminAuth:!1,defaultLinkTtlSeconds:14400,maxActiveLinks:10,defaultDeploymentType:"nodes"};a(fa,"isDeploymentType");a(dn,"sanitizePartial");a(ma,"readDiskFile");a(ln,"mergeAiAccessSettings");a(ur,"settingsJsPartial");nt=class{constructor(t,n){this.paths=t;this.filePath=(0,bo.join)(t.root,"settings.json"),this.settingsJs=ur(n),this.disk=ma(this.filePath),this.cached=ln(this.settingsJs,this.disk)}static{a(this,"SettingsStore")}filePath;disk;settingsJs;cached;getEffective(){return this.cached}getDisk(){return{...this.disk}}update(t){let n=dn(t);return this.disk=n,this.persist(),this.cached=ln(this.settingsJs,this.disk),this.cached}patch(t){return this.update({...this.disk,...dn(t)})}persist(){let t=tt(this.disk,ua,{kind:"settings",pretty:!0});an(this.paths.root,this.filePath,t)}}});function un(e){fr=e}function Y(e){return e.adminAuth!=null&&e.adminAuth!==!1}function se(e){if(fr){let n=fr.getEffective();return{enabled:n.enabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,defaultLinkTtlSeconds:n.defaultLinkTtlSeconds,defaultDeploymentType:n.defaultDeploymentType,maxActiveLinks:n.maxActiveLinks}}let t=ln(ur(e.aiAccess),{});return{enabled:t.enabled,allowWithoutAdminAuth:t.allowWithoutAdminAuth,defaultLinkTtlSeconds:t.defaultLinkTtlSeconds,defaultDeploymentType:t.defaultDeploymentType,maxActiveLinks:t.maxActiveLinks}}function ie(e){let t=se(e);return t.enabled?Y(e)?!0:t.allowWithoutAdminAuth:!1}var fr,Ae=C(()=>{"use strict";pn();fr=null;a(un,"bindSettingsStore");a(Y,"isAdminAuthEnabled");a(se,"resolveAiAccessSettings");a(ie,"areAiFeaturesEnabled")});function z(e){return!e||e==="/"?"/":e.endsWith("/")?e.slice(0,-1):e}function me(e){let t=z(e);return t==="/"?"/mcp/api/v1":`${t}/mcp/api/v1`}function Pe(e){let t=z(e);return t==="/"?"/mcp/rpc":`${t}/mcp/rpc`}function ga(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 rt(e){let t=ga(e);return t.includes("*")?"*":t.includes("read")?"read":null}function ha(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 vo(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 Mt(e,t){let n="read";return(r,o,s)=>{if(!Y(e.settings)){s();return}let i=t==="api"?vo(o):null;e.auth.needsPermission(n)(r,o,d=>{if(i?.(),d){s(d);return}if(!r.user){if(t==="page"){o.redirect(z(e.settings.httpAdminRoot));return}o.status(401).json({error:"Authentication required",code:"auth_required"});return}if(!rt(r)){o.status(401).json({error:"Insufficient permissions",code:"insufficient_permissions"});return}s()})}}function ko(e,t){let n=Y(e.settings),r=se(e.settings),o=ie(e.settings),s=null;return n?s=rt(t):o&&(s="*"),{adminAuthEnabled:n,aiFeaturesEnabled:o,allowWithoutAdminAuth:r.allowWithoutAdminAuth,permission:s,capabilities:ha(s,o)}}function fn(e){return(t,n,r)=>{if(!ie(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 mr(e){return(t,n,r)=>{if(!Y(e.settings)){if(ie(e.settings)){r();return}n.status(403).json({error:"AI Access features are disabled",code:"ai_features_disabled"});return}if(rt(t)!=="*"){n.status(403).json({error:"Full admin permission (*) is required for this action",code:"insufficient_permissions"});return}r()}}function ya(e){return e.startsWith("aia_")}function wa(e){return ya(e)?!0:e.length<24}function ba(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 Ao(e,t,n,r){if(!Y(e.settings)){r();return}let o={error:Q,code:ge},s=vo(n,o,j);e.auth.needsPermission("read")(t,n,i=>{if(s(),i){r(i);return}if(!n.headersSent){if(!t.user||!rt(t)){n.setHeader("WWW-Authenticate",j),n.status(401).json(o);return}r()}})}function So(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?(Ht(t,r,l.link.kind),"wrong_kind"):(e.accessLink=l.link,"ok");if(l.status==="expired")return jt(e,t,o,l.expiresAt),"invalid";if(wa(d)){t.setHeader("WWW-Authenticate",j);let c=ba(d);return t.status(401).json({error:Me,code:$t,...c?{hint:c}:{}}),"invalid"}return"continue"}function xo(e,t,n="rest"){return(r,o,s)=>{if(!ie(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=So(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||Ao(e,r,o,s)}}function To(e,t,n="rest"){return(r,o,s)=>{let i=So(r,o,t,n,e.settings.httpAdminRoot);if(i==="ok"){s();return}i==="invalid"||i==="wrong_kind"||Ao(e,r,o,s)}}var Se=C(()=>{"use strict";$e();Ae();a(z,"normalizeHttpAdminRoot");a(me,"mcpApiBase");a(Pe,"mcpRpcPath");a(ga,"scopesFromRequest");a(rt,"resolvePermission");a(ha,"capabilitiesFor");a(vo,"interceptNeedsPermissionUnauthorized");a(Mt,"requireAdminSession");a(ko,"describeAccess");a(fn,"requireAiFeatures");a(mr,"requireStarPermission");a(ya,"looksLikeAccessLinkToken");a(wa,"looksLikeFailedAccessLinkAttempt");a(ba,"truncatedTokenHint");a(Ao,"continueAsAdminSessionOrAccessLinkMissing");a(So,"tryAccessLinkBearer");a(xo,"requireAdminReadOrAccessLink");a(To,"requireAdminSessionOrAccessLink")});function va(e,t){let n=z(e);return`${n==="/"?"/mcp/a":`${n}/mcp/a`}/${t}`}function ka(e){return!!e.app.get("trust proxy")}function _o(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 Aa(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 Lo(e){gr=Aa(e)}function Ro(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],!Ta.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)||!xa.test(t)&&!Sa.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 Co(e){let t=e?.trim().toLowerCase();return t==="http"||t==="https"?t:void 0}function _a(e){let t=e.socket;return t?.encrypted?"https":t?"http":Co(e.protocol)??"http"}function hr(e){if(gr)return gr;let t=ka(e),n=t?_o(e,"x-forwarded-host"):void 0,r=e.get("host")?.trim(),o=Ro(n)?n:Ro(r)?r:void 0;if(!o)return;let s=(t?Co(_o(e,"x-forwarded-proto")):void 0)??_a(e);try{return new URL(`${s}://${o}`).origin}catch{return}}function Bt(e,t){let n=hr(e);return n?`${n}${t}`:t}function yr(e,t,n){return Bt(e,va(t,n))}function wr(e){return e.replace(/\/mcp\/a\/[^/?#]+/gi,"/mcp/a/<redacted>")}var ot,Eo,gr,Po,Sa,xa,Ta,xe=C(()=>{"use strict";Se();ot=8192,Eo=1800*1e3;a(va,"mcpAccessLinkPath");a(ka,"isTrustProxyEnabled");a(_o,"firstForwardedValue");a(Aa,"normalizePublicBaseUrl");a(Lo,"bindPublicBaseUrl");Po="[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?",Sa=new RegExp(`^${Po}(?:\\.${Po})*$`),xa=/^(?:(?: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)$/,Ta=/^\[[0-9A-Fa-f:.]+\]$/;a(Ro,"isValidHostHeader");a(Co,"validScheme");a(_a,"connectionScheme");a(hr,"requestPublicOrigin");a(Bt,"absolutizePath");a(yr,"buildAccessLinkUrl");a(wr,"redactAccessLinkTokenInPath")});function Na(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 br(e,t,n,r){e.setHeader("WWW-Authenticate",j),e.status(401).json({error:t,code:n,...r?{hint:r}:{}})}function Ia(e){let t=e??"";for(;t.endsWith("/");)t=t.slice(0,-1);return`${t}/mcp#links`}function Fa(e,t,n,r){let o=Bt(e,Ia(t));return{error:Pa,code:Ea,...n?{expiredAt:n}:{},accessLinksUrl:o,hint:r??`Ask the user to create a new access link in Node-RED on the /mcp page \u2192 Access links (${o}).`}}function jt(e,t,n,r,o){t.setHeader("WWW-Authenticate",j),t.status(401).json(Fa(e,n,r,o))}function Oa(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 mn(e){return e==="get_link"?"Please open Node-RED \u2192 /mcp \u2192 Access links and create a get_link access URL, then send me that /mcp/a/<token> link.":`Please open Node-RED \u2192 /mcp \u2192 Access links and create a kind=${e} token, then send me the Bearer token (not a get_link path URL).`}function Ma(e,t){let n=Da;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: ${mn("get_link")}`:e==="rest"?`This REST endpoint requires a rest token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${mn("rest")}`:`This MCP endpoint requires an mcp token. This token is kind=${t} \u2014 ${n}. What to tell the user: ${mn("mcp")}`}function Ht(e,t,n){e.setHeader("WWW-Authenticate",j),e.status(403).json({error:`Access link kind mismatch: expected ${t}, got ${n}`,code:Ca,expectedKind:t,actualKind:n,hint:Ma(t,n),tellUser:mn(t)})}function st(e,t,n){return(r,o,s)=>{if(!ie(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=Na(r.headers.authorization);if(i.kind==="basic"){br(o,Ra,La);return}if(i.kind==="missing"||i.kind==="other"){br(o,Q,ge);return}let d=t.validateResult(i.token);if(d.status==="expired"){jt(r,o,e.settings.httpAdminRoot,d.expiresAt);return}if(d.status!=="active"){br(o,Me,$t,Oa(i.token));return}if(d.link.kind!==n){Ht(o,n,d.link.kind);return}r.accessLink=d.link,s()}}var Me,Pa,Q,Ra,j,ge,$t,Ea,La,Ca,Da,$e=C(()=>{"use strict";xe();Ae();Me="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.",Pa="Access link token has expired. Ask the user for a new access link.",Q="Access link Bearer token is missing. Ask the user for a new access link and send Authorization: Bearer <token>.",Ra="Authorization must use the Bearer scheme (Authorization: Bearer <access-link-token>), not Basic. Ask the user for a new access link.",j='Bearer realm="node-red-ai-access", charset="UTF-8"',ge="access_link_missing",$t="access_link_invalid",Ea="token_expired",La="bearer_scheme_required",Ca="wrong_token_kind";a(Na,"parseAuthorizationHeader");a(br,"sendUnauthorized");a(Ia,"accessLinksPagePath");a(Fa,"tokenExpiredBody");a(jt,"sendTokenExpired");a(Oa,"truncatedBearerHint");Da="ask the owner: Node-RED /mcp \u2192 Access links \u2192 create kind=rest/mcp (or get_link)";a(mn,"whatToTellUserWrongTokenKind");a(Ma,"wrongTokenKindHint");a(Ht,"sendWrongTokenKind");a(st,"requireAccessLink")});var F,I,ae,he,gn,de=C(()=>{"use strict";F=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}},I=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}},ae=class extends Error{static{a(this,"AdapterUnavailableError")}code="adapter_unavailable";statusCode=502;constructor(t){super(t),this.name="AdapterUnavailableError"}},he=class extends Error{static{a(this,"PluginMisconfiguredError")}code="plugin_misconfigured";statusCode=502;constructor(t){super(t),this.name="PluginMisconfiguredError"}},gn="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 Te(e,t){if(!e||!$a.test(e)||e.includes(".."))throw new Error(`Invalid ${t}`);return e}function hn(e){if(!e||!ja.test(e)||e.includes(".."))throw new Error("Invalid proposal id");return e}function vr(e,t){let n=(0,je.basename)(t);if(!n||n!==t||n.includes(".."))throw new Error("Invalid file name");let r=(0,je.resolve)(e),o=(0,je.resolve)(r,n);if(o!==r&&!o.startsWith(r+je.sep))throw new Error("Path escapes storage directory");return o}function Io(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 Ha){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=Io(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>E)throw new M(`flow.${o}`,d,E);n[o]=JSON.parse(i)}let r=Buffer.byteLength(JSON.stringify(n),"utf8");if(r>E)throw new M("flow",r,E);return n}function Fo(e){let t={};for(let n of Ba){if(!(n in e))continue;let r=e[n];if(qa.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=Io(r,2e3);o!==void 0&&(t[n]=o)}return t}var je,$a,ja,Ha,Ba,qa,yn=C(()=>{"use strict";je=require("path");$();$a=/^[A-Za-z0-9_.-]+$/,ja=/^prop-[A-Za-z0-9_-]+$/,Ha=["id","type","label","disabled","info","env"],Ba=["proposalId","flowId","expectedRev","resultRev","reason","code","currentRev","title","displayName","flowLabel","linkId","label","expiresAt","createdAt","ttlSeconds","kind"],qa=new Set(["ttlSeconds"]);a(Te,"assertSafeSegment");a(hn,"assertSafeProposalId");a(vr,"resolveUnderDir");a(Io,"sanitizeString");a(it,"sanitizeFlowTab");a(Fo,"sanitizeAuditDetails")});function Ua(e){return e==="pending"||e==="approved"||e==="rejected"||e==="failed"||e==="withdrawn"||e==="expired"||e==="superseded"}function Oo(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e.slice(0,Ga)){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 Do,Re,Mo,J,G,at,qt,Ga,wn,dt=C(()=>{"use strict";Do=require("crypto"),Re=require("fs"),Mo=require("path");yn();$();J=class extends Error{static{a(this,"ProposalNotFoundError")}code="proposal_not_found";statusCode=404;constructor(t){super(`Proposal '${t}' was not found`),this.name="ProposalNotFoundError"}},G=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"}},at=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"}},qt=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(Ua,"isProposalStatus");Ga=50;a(Oo,"sanitizeProposalWarnings");wn=class{constructor(t,n={}){this.paths=t;this.now=n.now??(()=>Date.now()),this.randomId=n.randomId??(()=>(0,Do.randomBytes)(12).toString("hex")),this.maxProposalBytes=n.maxProposalBytes??E}static{a(this,"ProposalStore")}inFlight=new Set;now;randomId;maxProposalBytes;fileFor(t){let n;try{n=hn(t)}catch{throw new J(t)}return vr(this.paths.proposals,`${n}.json`)}toDiskRecord(t){let n=hn(t.id),r=Te(String(t.expectedRev).slice(0,200),"expectedRev"),o=Te(String(t.payload.flowId).slice(0,200),"flowId"),s=it(t.payload.flow),i=t.baseFlow!==void 0?it(t.baseFlow):void 0,d={kind:"access-link",linkId:Te(String(t.createdBy.linkId).slice(0,200),"linkId")};typeof t.createdBy.label=="string"&&(d.label=t.createdBy.label.replace(/\0/g,"").slice(0,200));let l={id:n,type:"replace_flow",status:Ua(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=Oo(t.warnings);if(c&&(l.warnings=c),t.decidedBy)if(t.decidedBy.kind==="access-link"){let p={kind:"access-link",linkId:Te(String(t.decidedBy.linkId).slice(0,200),"linkId")};typeof t.decidedBy.label=="string"&&(p.label=t.decidedBy.label.replace(/\0/g,"").slice(0,200)),l.decidedBy=p}else{let p={kind:"admin"};typeof t.decidedBy.username=="string"&&(p.username=t.decidedBy.username.replace(/\0/g,"").slice(0,200)),l.decidedBy=p}return typeof t.reason=="string"&&(l.reason=t.reason.replace(/\0/g,"").slice(0,2e3)),typeof t.resultRev=="string"&&(l.resultRev=Te(t.resultRev.slice(0,200),"resultRev")),l}writeAtomic(t,n){let r=this.fileFor(t),o=this.toDiskRecord(n),s=tt(o,this.maxProposalBytes,{kind:"proposal",pretty:!0});an(this.paths.root,r,s)}create(t){let n=new Date(this.now()).toISOString(),r=hn(`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=Oo(t.warnings);return s&&(o.warnings=s),this.writeAtomic(r,o),this.get(r)}get(t){let n=this.fileFor(t);if(!(0,Re.existsSync)(n))throw new J(t);return JSON.parse((0,Re.readFileSync)(n,"utf8"))}list(t){if(!(0,Re.existsSync)(this.paths.proposals))return[];let n=(0,Re.readdirSync)(this.paths.proposals).filter(o=>o.endsWith(".json")),r=[];for(let o of n){let s=(0,Mo.basename)(o);if(!(s!==o||s.includes("..")))try{let i=JSON.parse((0,Re.readFileSync)(vr(this.paths.proposals,s),"utf8"));if(t?.linkId&&i.createdBy.linkId!==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 qt(t);let n=this.get(t);if(n.status!=="pending")throw new G(t,n.status);return this.inFlight.add(t),n}releaseLock(t){this.inFlight.delete(t)}markApproved(t,n,r){let o=this.get(t);return o.status="approved",o.decidedBy=n,o.resultRev=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markRejected(t,n,r){let o=this.get(t);return o.status="rejected",n&&(o.decidedBy=n),o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markFailed(t,n,r){let o=this.get(t);return o.status="failed",n&&(o.decidedBy=n),o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markWithdrawn(t,n,r){let o=this.get(t);if(o.status!=="pending")throw new G(t,o.status);return o.status="withdrawn",o.decidedBy=n,o.reason=r,o.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,o),this.get(t)}markClosed(t,n,r,o){let s=this.get(t);if(s.status!=="pending")throw new G(t,s.status);return s.status=n,o&&(s.decidedBy=o),s.reason=r,s.updatedAt=new Date(this.now()).toISOString(),this.writeAtomic(t,s),this.get(t)}}});function $o(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 bn(e,t,n){let r=e.find(d=>d.id===t);if(!r||r.type!=="tab"&&r.type!=="subflow")throw new I(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({...$o(l,`flowData.nodes[${d}]`),z:t});for(let[d,l]of(n.configs??[]).entries())s.push({...$o(l,`flowData.configs[${d}]`),z:t});return[...e.filter(d=>d.id!==t&&d.z!==t),o,...s]}function Ut(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 lt(e,t){if(Array.isArray(t)){let d=t.find(p=>p&&typeof p=="object"&&p.id===e&&(p.type==="tab"||p.type==="subflow"));if(!d)throw new I(e);let l=t.filter(p=>p&&typeof p=="object"&&p.id!==e&&p.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 I(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 vn=C(()=>{"use strict";de();a($o,"asFlowNode");a(bn,"replaceFlowTabInFlatFlows");a(Ut,"assertFlowsSnapshot");a(lt,"normalizeFlowTab")});function xr(e){return e?Wa.has(e)?!0:e.startsWith("subflow:"):!1}function Ee(e,t="node.type"){if(e==null||e==="")throw new g(`${t} is required`,"missing_node_type");if(typeof e!="string")throw new g(`${t} must be a non-empty string (got ${typeof e})`,"invalid_node_type",{details:{type:e,got:typeof e}});if(!e.trim())throw new g(`${t} is required`,"missing_node_type");return e}function Qa(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 p=1;p<=s;p++){let u=c===r.charAt(p-1)?0:1;d[p]=Math.min((d[p-1]??0)+1,(i[p]??0)+1,(i[p-1]??0)+u)}for(let p=0;p<=s;p++)i[p]=d[p]??0}return i[s]??s}function Za(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 td(e){return Math.max(2,Math.floor(e.length/3))}function Tr(e,t,n=5){if(typeof e!="string"||!e)return[];let r=td(e);return t.filter(s=>typeof s=="string"&&s!==e&&!ed.has(s)&&!s.startsWith("_")&&!s.startsWith("subflow:")).map(s=>({c:s,dist:Qa(e,s),prefix:Za(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 nd(e){return e.endsWith("/")?e.slice(0,-1):e}function Wo(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Go.createRequire)(e)}function rd(){try{let t=Wo()("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}function zo(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 _r(e=Ya,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 Jo(e={}){if(rd())try{let n=Wo()("@node-red/runtime");if(n.nodes&&typeof n.nodes.getNodeList=="function")return new Sr(n.nodes)}catch{}if(e.baseUrl)return new Ar({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 Pr(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 od(e,t){let n=t.toLowerCase();return[e.type,e.module,e.category??"",e.description??""].join(`
|
|
4
|
+
`).toLowerCase().includes(n)}async function An(e,t={}){let n=t.limit??za,{byType:r}=await Pr(e),o=[...r.values()].sort((p,u)=>p.type.localeCompare(u.type)),s=t.q?.trim();s&&(o=o.filter(p=>od(p,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 sd(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 p=e.indexOf(">",l);if(p<0)return null;let u=o.indexOf("</script",p+1);if(u<0)return null;let f=u+8;for(;f<e.length&&/\s/.test(e.charAt(f));)f+=1;if(e.charAt(f)!==">"){s=u+1;continue}return e.slice(p+1,u)}return null}function Ho(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 p=c+r.length;for(;p<e.length&&/\s/.test(e.charAt(p));)p+=1;e.charAt(p)===">"?(s=p+1,o+=" "):s=c+r.length}return o}function id(e,t=2e3){let n=Ho(e,"script");return n=Ho(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 ad(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:Bo(e,d,i)}return Bo(e,n.index,r)}catch{return null}}function Bo(e,t,n){let r=e.slice(t),o=r.indexOf("{");if(o<0)return{type:n};let s=Gt(r.slice(o));if(!s)return{type:n};let i=qo(s,"category"),d=qo(s,"color"),l=Uo(s,"inputs"),c=Uo(s,"outputs"),p=ld(s),u=[];if(p){for(let y of Object.values(p))if(y&&typeof y=="object"&&!Array.isArray(y)){let x=y.type;typeof x=="string"&&x.length>0&&u.push(x)}}let f,m=sd(e,n);m&&(f=id(m));let h={type:n};return i&&(h.category=i),d&&(h.color=d),l!==void 0&&(h.inputs=l),c!==void 0&&(h.outputs=c),p&&(h.defaults=p),u.length>0&&(h.requiredConfigNodeTypes=[...new Set(u)]),f&&(h.help=f),h}function dd(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 p=/^(#{1,3})\s*(inputs?|outputs?)\b[\s:]*\n?([\s\S]*)$/i.exec(c);if(p){let u=(p[2]??"").toLowerCase(),f=(p[3]??"").trim();u.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 Gt(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 qo(e,t){let n=new RegExp(`\\b${t}\\s*:\\s*['"]([^'"]+)['"]`);return e.match(n)?.[1]}function Uo(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 ld(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=Gt(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=Gt(o.slice(d.index+d[0].length-1));if(!c)continue;let p={},u=/\btype\s*:\s*['"]([^'"]+)['"]/.exec(c);u?.[1]&&(p.type=u[1]),/\brequired\s*:\s*true\b/.test(c)&&(p.required=!0);let f=/\bvalue\s*:\s*/.exec(c);if(f){let m=c.slice(f.index+f[0].length).trimStart();p.value=ud(m)}s[l]=p}return Object.keys(s).length>0?s:void 0}function cd(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 pd(e){return e==="_DEFAULT_"?"":e}function kn(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=cd(t,o+1);s+=d.char,o=d.next;continue}if(i===r)break;s+=i,o+=1}return pd(s)}if(t.startsWith("[")){let r=Ko(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=kn(d);s.push(l);let c=kr(d);i+=c>0?c:1}return s}if(t.startsWith("{")){let r=Gt(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=kn(o.slice(i));d=typeof c=="string"?c:"",i+=kr(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]=kn(l),i+=kr(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 kr(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=Ko(t);return n+(o?.length??1)}if(t.startsWith("{")){let o=Gt(t);return n+(o?.length??1)}let r=/^(?:-?\d+(?:\.\d+)?|true|false|null)/.exec(t);return r?n+r[0].length:n+1}function Ko(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 ud(e){return kn(e)}function fd(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 Rr(e,t){let n=e.toLowerCase();return t.includes(e)?e:t.find(r=>r.toLowerCase()===n)}async function He(e,t,n={}){if(!t||typeof t!="string")throw new g("Missing node type","missing_node_type");let r=n.includeHelp!==!1;if(xr(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=fd(o,t);if(!i){let l=Rr(t,s);throw l&&l!==t?new g(`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 g(`Node type '${t}' was not found`,"node_type_not_found",{statusCode:404,details:{type:t,nearest:Tr(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=ad(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=dd(c.help)))}}catch{}return d}async function Vo(e,t){let{allNames:n}=await Pr(e),r=new Set(n),o=new Set,s=a(l=>{let c=Ee(l,"node.type");if(o.has(c)||xr(c)){o.add(c);return}if(o.add(c),r.has(c))return;let p=Rr(c,n);throw p&&p!==c?new g(`Unknown node type '${c}' \u2014 did you mean '${p}'? Types are case-sensitive.`,"unknown_node_type",{details:{type:c,didYouMean:p,caseSensitive:!0,nearest:[p]}}):new g(`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:Tr(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 p of c.nodes)i(p);if(Array.isArray(c.configs))for(let p of c.configs)i(p)},"visitTab")(t)}async function Xo(e,t){let n=Ee(t,"node.type");if(xr(n))return;let{allNames:r}=await Pr(e);if(r.includes(n))return;let o=Rr(n,r);throw o&&o!==n?new g(`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 g(`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:Tr(n,r,5)}})}async function md(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 gd(e,t){let n=Date.now();if(le&&n-le.fetchedAt<Xa)return le.body;let r=le?.etag,o=await e(t,r);if(o.status===304&&le)return le={...le,fetchedAt:n,...o.etag?{etag:o.etag}:{}},le.body;if(!o.body||!Array.isArray(o.body.modules)){if(le)return le.body;throw new g("Node-RED catalogue unavailable","catalogue_unavailable",{statusCode:502})}return le={fetchedAt:n,body:o.body,...o.etag?{etag:o.etag}:{}},o.body}async function Sn(e,t,n={}){let r=t.trim();if(!r)throw new g("q query parameter is required","missing_query");let o=n.limit??Ja,s=n.catalogueUrl??Va,i=n.catalogueFetcher??md,d=await gd(i,s),l=Array.isArray(d.modules)?d.modules:[],c=await e.listNodeSets(),p=new Set(c.flatMap(y=>{let x=[y.module,y.id];return y.id.includes("/")&&x.push(y.id.split("/")[0]??y.module),x.map(k=>k.toLowerCase())})),u=r.toLowerCase(),f=[];for(let y of l){let x=y.id.toLowerCase(),k=(y.keywords??[]).map(It=>String(It).toLowerCase()),re=(y.description??"").toLowerCase(),U=hd(u,x,k,re);if(U<=0)continue;let L={module:y.id,installed:p.has(x)};typeof y.description=="string"&&(L.description=y.description),typeof y.version=="string"&&(L.version=y.version),typeof y.updated_at=="string"&&(L.updated=y.updated_at),typeof y.downloads=="number"&&(L.downloads=y.downloads),typeof y.url=="string"&&(L.link=y.url),f.push({item:L,score:U})}f.sort((y,x)=>x.score-y.score||(x.item.downloads??0)-(y.item.downloads??0)||y.item.module.localeCompare(x.item.module));let m=f.length,h=m>o;return{q:r,modules:f.slice(0,o).map(y=>y.item),total:m,truncated:h,hint:Ka}}function hd(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 Go,Wa,za,Ja,Ka,Va,Xa,Ya,ed,Ar,jo,Sr,le,Be=C(()=>{"use strict";Go=require("module");O();Wa=new Set(["tab","subflow","group","junction"]),za=100,Ja=30,Ka="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.",Va="https://catalogue.nodered.org/catalogue.json",Xa=3600*1e3,Ya=["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(xr,"isStructuralNodeType");a(Ee,"requireNodeTypeName");a(Qa,"editDistance");a(Za,"sharedPrefixLength");ed=new Set(["unknown","tab","subflow","group"]);a(td,"nearestDistanceCutoff");a(Tr,"nearestNodeTypes");a(nd,"trimTrailingSlash");a(Wo,"createPluginRequire");a(rd,"isRuntimeSettingsAvailable");a(zo,"normalizeNodeSet");a(_r,"createStaticNodeTypesAdapter");Ar=class{static{a(this,"HttpNodeTypesAdapter")}baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=nd(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=zo(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()}},jo={user:null,req:null},Sr=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(jo))}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=zo(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",...jo}));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(Jo,"createNodeTypesAdapter");a(Pr,"flattenInstalledTypes");a(od,"matchesQuery");a(An,"listNodeTypes");a(sd,"extractHelpScriptBody");a(Ho,"removeHtmlBlocks");a(id,"stripHelpText");a(ad,"parseRegisterTypeFromHtml");a(Bo,"parseRegisterTypeAt");a(dd,"splitHelpSections");a(Gt,"extractBalancedObject");a(qo,"matchStringProp");a(Uo,"matchNumberProp");a(ld,"parseDefaultsBlock");a(cd,"unescapeJsStringEscape");a(pd,"normalizeDefaultValue");a(kn,"parseJsLiteral");a(kr,"measureJsLiteralLength");a(Ko,"extractBalancedArray");a(ud,"parseLooseValue");a(fd,"findSetForType");a(Rr,"findCaseInsensitiveType");a(He,"getNodeTypeDetail");a(Vo,"assertKnownNodeTypes");a(Xo,"assertKnownNodeType");le=null;a(md,"defaultCatalogueFetcher");a(gd,"loadCatalogue");a(Sn,"searchPalette");a(hd,"paletteMatchScore")});function Le(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 g(`${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 g(t.error,t.code,{statusCode:o,details:r})}function Er(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 yd(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 Qo(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 ${yd(c)}; node ids must be unique across all flows`,nodeId:l})}return o}function Yo(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 bd(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]);for(let r of n)if(!wd.has(r)&&JSON.stringify(e[r])!==JSON.stringify(t[r]))return!0;return!1}function vd(e,t){let n=Yo(t);if(!e)return new Set(n.keys());let r=Yo(e),o=new Set;for(let[s,i]of n){let d=r.get(s);(!d||bd(d,i))&&o.add(s)}return o}function Zo(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(...Er(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{Ee(c.type,"node.type")}catch(p){p instanceof g&&n.push({code:p.code,error:p.message,...typeof c.id=="string"?{nodeId:c.id}:{}})}"wires"in c&&ct(c.wires,n,typeof c.id=="string"?c.id:void 0)}return n}function ct(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 kd(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 He(t,n,{includeHelp:!1})).outputs}catch{return}}function Ad(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 Sd(e){return Array.isArray(e)&&e.some(t=>typeof t=="string"&&t.length>0)}function xd(e,t){return t<=0?!1:!("wires"in e)||e.wires===void 0?!0:Array.isArray(e.wires)?!e.wires.some(Sd):!1}async function Lr(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 kd(d,t),c=typeof d.type=="string"?d.type:"node",p=!i||i.has(d.id);if("wires"in d&&d.wires!==void 0){if(!ct(d.wires,r,d.id))continue;let u=d.wires;if(l!==void 0){let f=Ad(d,l,u);f?(f.cause="excess_ports",r.push(f)):p&&u.length<l&&o.push({code:"incomplete_wires",error:`${c} '${d.id}' declares ${l} outputs but wires has only ${u.length} port(s)`,nodeId:d.id,type:c})}for(let f of u)for(let m of f){let h=String(m);s.has(h)||r.push({code:"invalid_wires",error:`Wire target '${h}' on node '${d.id}' was not found in this flow (add the node in the same patch or use an existing id)`,nodeId:d.id,cause:"missing_target",targetId:h})}}p&&l!==void 0&&xd(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 Cr(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 Td(e,t){return e.filter(n=>n.type===t).map(n=>n.id)}async function Nr(e,t,n){let r=[],o=[...t];for(let s of e){let i;try{i=Ee(s.type,"node.type")}catch{continue}let d;try{d=await He(n,i,{includeHelp:!1})}catch{continue}let l=d.defaults;if(!l||typeof l!="object")continue;let c=new Set(d.requiredConfigNodeTypes??[]);for(let[p,u]of Object.entries(l)){if(!u||typeof u!="object"||Array.isArray(u))continue;let f=u.type;if(typeof f!="string"||!f)continue;let h=u.required===!0||c.has(f),y=s[p],x=Td(o,f);if(typeof y!="string"||y.length===0){h&&r.push({code:"missing_config_node",error:`Node '${s.id}' (${i}) has no ${f} on '${p}' yet \u2014 configure before deploy if needed`,nodeId:s.id,type:i,property:p,configType:f,candidates:x});continue}o.find(re=>re.id===y)?.type!==f&&r.push({code:"missing_config_node",error:`Node '${s.id}' property '${p}' does not resolve to a ${f} config yet`,nodeId:s.id,type:i,property:p,configType:f,candidates:x})}}return r}async function es(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(...Cr(r,n));let d=t.baseFlow?vd(t.baseFlow,e):void 0,l=await Lr(r,t.nodeTypes,{...d?{softWarningNodeIds:d}:{}});if(s.push(...l.errors),i.push(...l.warnings),t.nodeTypes){let c=await Nr(r,o,t.nodeTypes);d?i.push(...c.filter(p=>!p.nodeId||d.has(p.nodeId))):i.push(...c)}return{errors:s,warnings:i}}var wd,Ir=C(()=>{"use strict";O();Be();a(Le,"throwValidationIssues");a(Er,"collectDuplicateNodeIdIssues");a(yd,"describeLiveNode");a(Qo,"collectCrossFlowDuplicateIssues");wd=new Set(["x","y","w","h","g","id","z"]);a(Yo,"collectNodesMap");a(bd,"nodeContentChanged");a(vd,"changedNodeIds");a(Zo,"collectFullFlowShapeIssues");a(ct,"assertValidWiresShape");a(kd,"resolveNodeOutputCount");a(Ad,"wireCountIssue");a(Sd,"portHasTargets");a(xd,"nodeIsUnwired");a(Lr,"collectWireValidation");a(Cr,"collectZMismatchIssues");a(Td,"configCandidates");a(Nr,"collectConfigWarnings");a(es,"validateFlowTab")});function Ce(e,t="node"){return typeof e=="string"&&e.trim()?e.trim():t}function xn(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 ns(e,t){e[ts]=t}function Rd(e){if(!e)return;let t=e[ts];return t instanceof Map?t:void 0}function Ed(e){return typeof e!="string"||!e.trim()?new Set:new Set(e.split(",").map(t=>t.trim()).filter(Boolean))}function Ld(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 K(e){let t=Ed(e.include);return Ld(e.include_payload)&&(t.add("payload"),t.add("baseFlow")),t}function N(e,t){let n={req:e};t!==void 0&&(n.httpAdminRoot=t);let r=Rd(e);return r&&(n.liveFlowLabels=r),n}function pt(e){return typeof e.name=="string"&&e.name.trim()?e.name.trim():typeof e.label=="string"&&e.label.trim()?e.label.trim():""}function Wt(e){return`"${e.replace(/"/g,"'")}"`}function ut(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 Cd(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 Nd(e,t){let n=pt(e),r=pt(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 Fr(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&&Cd(d,i)&&o.push({before:d,after:i})}return{added:n,removed:r,modified:o}}function Tn(e){return e===1?"node":"nodes"}function Id(e){return e.length===1&&e[0]?`adds 1 ${Ce(e[0].type)}`:`adds ${e.length} ${Tn(e.length)}`}function Fd(e){return`deletes ${e} ${Tn(e)}`}function Od(e){return`edits ${e} ${Tn(e)}`}function Dd(e){let t=[];e.removed>0&&t.push(Fd(e.removed));let n=e.added??[],r=e.addedCount??n.length;r>0&&t.push(n.length===r?Id(n):`adds ${r} ${Tn(r)}`),(e.modified??0)>0&&t.push(Od(e.modified??0));let o=t.length?t.join(", "):"replaces nodes",s=e.flowLabel?.trim();return s?`Replace ${s}: ${o}`:`Replace flow: ${o}`}function Md(e){let{added:t,removed:n,modified:r}=e;if(t.length+n.length+r.length!==1)return;if(t[0]){let p=t[0],u=Ce(p.type),f=pt(p);return f?`Add ${u} node ${Wt(f)}`:`Add ${u} node`}if(n[0]){let p=n[0],u=Ce(p.type),f=pt(p);return f?`Remove ${u} node ${Wt(f)}`:`Remove ${u} node`}let s=r[0];if(!s)return;let i=Ce(s.after.type,Ce(s.before.type)),d=pt(s.before),l=pt(s.after);if(Nd(s.before,s.after)&&d&&l)return`Rename ${i} node ${Wt(d)} \u2192 ${Wt(l)}`;let c=l||d;return c?`Edit ${i} ${Wt(c)}`:`Edit ${i}`}function Or(e,t){return typeof e?.label=="string"&&e.label.trim()?e.label.trim():typeof t?.label=="string"&&t.label.trim()?t.label.trim():""}function _n(e,t){if(typeof e.flowLabel=="string"&&e.flowLabel.trim())return e.flowLabel.trim();let n=Or(e.payload?.flow,e.baseFlow);if(n)return n;let r=e.payload?.flowId;return r&&t?.has(r)?t.get(r)??"":""}function rs(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 Dr(e,t){let n=Fr(ut(e??{}),ut(t)),r=n.added.length+n.removed.length+n.modified.length,o=Or(t,e);if(r===0)return o?`Update ${o}`:"Update flow";if(n.removed.length>0)return Dd({...o?{flowLabel:o}:{},removed:n.removed.length,added:n.added,modified:n.modified.length});if(r>Pd)return o?`${r} changes in ${o}`:`${r} changes`;let s=Md(n)??(o?`Update ${o}`:"Update flow");return rs(s,o)}function $d(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 Mr(e,t){return`${e} \xB7 ${Hd(t)}`}function jd(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 Hd(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=jd(-n.getTimezoneOffset());return`${r} ${s} ${i}:${d} ${l}`}function Pn(e){if(typeof e!="string")return;let t=e.replace(/\0/g,"").replace(/[\u0001-\u001F\u007F]/g," ").replace(/\s+/g," ").trim().slice(0,_d).trim();return t.length>0?t:void 0}function Bd(e,t){let n=z(t);return`${n==="/"?"/mcp":`${n}/mcp`}#proposal=${encodeURIComponent(e)}`}function $r(e,t={}){let n=_n(e,t.liveFlowLabels),r=Pn(e.title),o=Dr(e.baseFlow,n&&!Or(e.payload.flow,e.baseFlow)?{...e.payload.flow,label:n}:e.payload.flow),s=Fr(ut(e.baseFlow??{}),ut(e.payload.flow)),i=$d(r,s.removed.length)?o:r??o;return rs(i,n)}function qd(e){return typeof e.storedTitle=="string"&&e.storedTitle?e.storedTitle:Pn(e.title)}function V(e,t={}){let n=$r(e,t),r=_n(e,t.liveFlowLabels),o=qd(e)??n,s=Bd(e.id,t.httpAdminRoot),i=t.req?Bt(t.req,s):s;return{...e,title:n,storedTitle:o,flowLabel:r,displayName:Mr(n,e.createdAt),reviewUrl:i,reviewPath:s}}function Ud(e){let t=Fr(ut(e.baseFlow??{}),ut(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 Gd(e){let t={kind:e.kind,linkId:e.linkId};return typeof e.label=="string"&&e.label.length>0&&(t.label=e.label),t}function Z(e,t={}){let n=V(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:Gd(n.createdBy),changeSummary:Ud(e)};return r.has("payload")&&(o.payload=n.payload),r.has("baseFlow")&&n.baseFlow!==void 0&&(o.baseFlow=n.baseFlow),n.warnings&&n.warnings.length>0&&(o.warnings=n.warnings),n.baseRevStale!==void 0&&(o.baseRevStale=n.baseRevStale),n.decidedBy!==void 0&&(o.decidedBy=n.decidedBy),n.reason!==void 0&&(o.reason=n.reason),n.resultRev!==void 0&&(o.resultRev=n.resultRev),o}function os(e,t={}){return e.map(n=>Z(n,t))}var _d,Pd,ts,ce=C(()=>{"use strict";xe();Se();_d=80,Pd=1;a(Ce,"nodeTypeText");ts=Symbol.for("ai-access.liveFlowLabels");a(xn,"liveFlowLabelsFromFlows");a(ns,"setRequestFlowLabels");a(Rd,"getRequestFlowLabels");a(Ed,"parseIncludeQuery");a(Ld,"isTruthyFlag");a(K,"resolveIncludeFromQuery");a(N,"presentOptionsFor");a(pt,"nodeLabel");a(Wt,"quoteName");a(ut,"collectNodes");a(Cd,"nodeContentChanged");a(Nd,"isRenameOnly");a(Fr,"classifyFlowChanges");a(Tn,"pluralNodes");a(Id,"describeAddedClause");a(Fd,"describeRemovedClause");a(Od,"describeModifiedClause");a(Dd,"buildReplaceFlowTitle");a(Md,"describeOneChange");a(Or,"flowLabelHint");a(_n,"resolveFlowLabel");a(rs,"ensureTitleIncludesFlowName");a(Dr,"autoGenerateProposalTitle");a($d,"shouldRefreshDisplayTitle");a(Mr,"buildProposalDisplayName");a(jd,"formatUtcOffset");a(Hd,"formatProposalSubmittedAt");a(Pn,"sanitizeProposalTitle");a(Bd,"buildProposalReviewPath");a($r,"resolveProposalTitle");a(qd,"storedTitleOf");a(V,"presentProposal");a(Ud,"buildChangeSummary");a(Gd,"presentCreatedByForApi");a(Z,"presentProposalForApi");a(os,"presentProposalsForApi")});function ss(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 zd(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let o of n)Wd.has(o)||JSON.stringify(e[o])!==JSON.stringify(t[o])&&r.push(o);return r.sort()}function is(e){let t=ss(e.baseFlow??{}),n=ss(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=zd(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 Jd(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(p=>p!=="*")),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 D(e,t,n={}){let r=0;for(let o of e.list())if(o.status==="pending"&&!(n.flowId&&o.payload.flowId!==n.flowId)&&o.expectedRev!==t)try{e.markClosed(o.id,"expired",Kd),r+=1}catch{}return r}function as(e,t,n,r){let o=`Superseded by approved proposal ${n} (resultRev ${r}). Re-read the flow and resubmit if still needed.`,s=0;for(let i of e.list())if(!(i.id===n||i.status!=="pending"||i.payload.flowId!==t))try{e.markClosed(i.id,"superseded",o),s+=1}catch{}return s}function ye(e,t,n){let r=is(t),o=[];for(let s of e.list()){if(s.id===t.id||s.status!=="pending"||s.payload.flowId!==t.payload.flowId)continue;let i=$r(s),d=n?s.createdBy.linkId===n:s.createdBy.linkId===t.createdBy.linkId,l=Jd(r,is(s)),c=s.expectedRev===t.expectedRev,p=l.nodeIds.length>0;if(!c&&!p)continue;let u={id:s.id,title:i,displayName:Mr(i,s.createdAt),mine:d};l.nodeIds.length>0&&(u.overlappingNodeIds=l.nodeIds),l.fields.length>0&&(u.overlappingFields=l.fields),o.push(u)}return{conflictingProposals:o}}function we(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 H(e){return e>0?{autoClosedCount:e}:{}}var Wd,Kd,Ne=C(()=>{"use strict";ce();Wd=new Set(["x","y","w","h","g","id","z"]);a(ss,"collectNodes");a(zd,"changedFieldKeys");a(is,"computeProposalTouches");a(Jd,"overlapBetween");Kd="Proposal expired: the live flow revision moved past expectedRev. Re-read the flow and resubmit.";a(D,"expireStalePendingProposals");a(as,"supersedePendingOnFlow");a(ye,"findConflictingProposals");a(we,"conflictWarningFields");a(H,"autoClosedFields")});var gs={};mo(gs,{applyFlowNodeOps:()=>qe,applyReplaceTextToValue:()=>ms,buildPatchedFlowTab:()=>zt,collectSetProps:()=>jr,decodeBase64UrlJson:()=>Jt,findTextOccurrences:()=>fs,interpretPatch:()=>Kt});function us(e){return e===null?"null":Array.isArray(e)?"array":typeof e}function Vd(e){if(typeof e!="object"||e===null||Array.isArray(e))throw new g(`add op needs node as an object (got ${us(e)})`,"invalid_node")}async function zt(e,t,n){return qe(e,t,n.map(r=>({op:"set",nodeId:r.nodeId,set:r.set})))}function fs(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 ms(e,t,n,r,o){if(ps.has(t))throw new g(`Cannot change protected node property '${t}' via replace_text`,"protected_property");if(typeof n!="string"||n.length===0)throw new g("find must be a non-empty string","missing_find");if(typeof r!="string")throw new g("replace must be a string","invalid_replace");if(e===void 0)throw new g(`Field '${t}' was not found on the node`,"field_not_found");if(typeof e!="string")throw new g(`Field '${t}' is not a string (got ${e===null?"null":typeof e})`,"field_not_string");let s=fs(e,n);if(s.length===0)throw new g(`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 g(`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 g(`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 g(`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 Xd(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 ${us(r)})`});continue}let o=r;(!o.id||typeof o.id!="string")&&t.push({code:"missing_node_id",error:"add op needs node.id"});try{Ee(o.type,"node.type")}catch(s){s instanceof g&&t.push({code:s.code,error:s.message,nodeId:o.id})}"wires"in o&&ct(o.wires,t,typeof o.id=="string"?o.id:void 0)}else n.op==="set"&&typeof n.set=="object"&&"wires"in n.set&&ct(n.set.wires,t,n.nodeId);return t}function Yd(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 qe(e,t,n,r={}){if(!t)throw new g("flow id is required","missing_flow_id");if(!n.length)throw new g("At least one node edit is required","missing_edits");let o=Xd(n);o.push(...Er([n.flatMap(h=>h.op==="add"?[h.node]:[])],"the batch (add ops)")),Le(o);let s=await e.getFlow(t);if(r.expectedRev!==void 0&&s.rev!==r.expectedRev){let h=Array.isArray(s.flow.nodes)?[...s.flow.nodes]:[];return{expectedRev:s.rev,flow:{...s.flow,id:s.flow.id,type:s.flow.type??"tab",nodes:h}}}let i=Array.isArray(s.flow.nodes)?[...s.flow.nodes]:[],d=Array.isArray(s.flow.configs)?[...s.flow.configs]:[],l={...s.flow,id:s.flow.id,type:s.flow.type??"tab",nodes:i,configs:d},c=i.map(h=>({...h})),p=[];for(let h of n)try{h.op==="add"&&r.nodeTypes&&await Xo(r.nodeTypes,h.node.type),ls(c,t,h)}catch(y){if(y instanceof g)p.push({code:y.code,error:y.message,...y.statusCode!==400?{statusCode:y.statusCode}:{},...y.details??{}});else throw y}p.push(...Cr(c,t));let u=Yd(n),f=await Lr(c,r.nodeTypes,{softWarningNodeIds:u});p.push(...f.errors),Le(p);for(let h of n)ls(i,t,h);let m=[...f.warnings];if(r.nodeTypes){let h=await Nr(i,d,r.nodeTypes);m.push(...h.filter(y=>!y.nodeId||u.has(y.nodeId)))}return{expectedRev:s.rev,flow:l,...m.length>0?{warnings:m}:{}}}function ds(e,t,n){if(!t||typeof t!="string")throw new g("Each edit needs a node id","missing_node_id");let r=e.findIndex(o=>o.id===t);if(r<0||!e[r])throw new g(`Node '${t}' was not found in flow '${n}'`,"node_not_found",{statusCode:404});return r}function ls(e,t,n){if(n.op==="add"){let i=n.node;if(!i.id||typeof i.id!="string")throw new g("add op needs node.id","missing_node_id");if(Ee(i.type,"node.type"),e.some(d=>d.id===i.id))throw new g(`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=ds(e,n.nodeId,t);e.splice(i,1);return}let r=ds(e,n.nodeId,t),o=e[r];if(!o)throw new g(`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 g(`Field '${n.field}' was not found on the node`,"field_not_found");let i=ms(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(ps.has(i))throw new g(`Cannot change protected node property '${i}' via GET propose`,"protected_property");if(i==="wires"){let l=[];ct(d,l,n.nodeId),Le(l)}s[i]=d}e[r]=s}function cs(e){if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean"||typeof e=="bigint")return String(e)}function jr(e){let t={},n=e.set;if(n&&typeof n=="object"&&!Array.isArray(n))for(let[r,o]of Object.entries(n)){let s=cs(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=cs(o);i!==void 0&&(t[s]=i)}}return t}function Jt(e){let t;try{t=Buffer.from(e,"base64url").toString("utf8")}catch{throw new g("patch must be base64url-encoded JSON","invalid_patch")}try{return JSON.parse(t)}catch{throw new g("patch JSON is malformed","invalid_patch_json")}}function Qd(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 Hr(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 Zd(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 g("occurrence must be a positive integer","invalid_occurrence");if(!Number.isFinite(t))throw new g("occurrence must be a positive integer","invalid_occurrence");return Math.trunc(t)}function el(e){let t=e.node;if(t!==void 0)return Vd(t),{op:"add",node:t};let n={...e};return delete n.op,{op:"add",node:n}}function tl(e){let t=Hr(e);if(!t)throw new g("replace_text op needs node id","missing_node_id");let n=typeof e.field=="string"?e.field:"";if(!n)throw new g("replace_text op needs field","missing_field");if(typeof e.find!="string")throw new g("replace_text op needs find string","missing_find");if(typeof e.replace!="string")throw new g("replace_text op needs replace string","invalid_replace");let r=Zd(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 nl(e){let t=Hr(e);if(!t)throw new g("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 rl(e){let t=typeof e.op=="string"?e.op.toLowerCase():"set";if(t==="add")return el(e);if(t==="remove"){let n=Hr(e);if(!n)throw new g("remove op needs id","missing_node_id");return{op:"remove",nodeId:n}}if(t==="replace_text")return tl(e);if(t!=="set")throw new g("nodes[].op must be 'add', 'remove', 'set', or 'replace_text'","invalid_patch");return nl(e)}function Kt(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new g("patch must be a JSON object","invalid_patch");let n=e;if(n.type==="replace_flow"||n.flow&&typeof n.flow=="object"){let r=Qd(n,t);if(!r)throw new g("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 g("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 g("Each patch.nodes entry must be an object","invalid_patch");o.push(rl(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 g("patch must include nodes[] edits or type/flow for replace_flow","invalid_patch")}var ps,Rn=C(()=>{"use strict";O();Ir();Be();ps=new Set(["id","type","z"]);a(us,"describeNonObjectNode");a(Vd,"assertAddNodeObject");a(zt,"buildPatchedFlowTab");a(fs,"findTextOccurrences");a(ms,"applyReplaceTextToValue");a(Xd,"collectOpShapeIssues");a(Yd,"touchedNodeIdsFromOps");a(qe,"applyFlowNodeOps");a(ds,"requireNodeIndex");a(ls,"applyOneNodeOp");a(cs,"stringifyQueryValue");a(jr,"collectSetProps");a(Jt,"decodeBase64UrlJson");a(Qd,"resolveReplaceFlowId");a(Hr,"resolveNodeId");a(Zd,"parseOccurrence");a(el,"parseAddOp");a(tl,"parseReplaceTextOp");a(nl,"parseSetOp");a(rl,"parseNodeOp");a(Kt,"interpretPatch")});function ft(e,t){return e.createdBy.linkId===t.id}function Br(e,t){return!t||t.length===0||t.includes(e)}function ol(e){return typeof e=="string"&&ys.has(e)}function gt(e,t){let n=sl[e],r=e!=="status"&&Array.isArray(t);return new g(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 En(e,t){if(Array.isArray(t))throw gt(e,t);return t}function il(e){return typeof e=="number"?Number.isInteger(e)?e:null:typeof e=="string"&&/^-?\d+$/.test(e.trim())?Number(e.trim()):null}function qr(e){return e==null||e===""}function al(e){if(!qr(e)){if(e==="true"||e==="1"||e===!0)return!0;if(e==="false"||e==="0"||e===!1)return!1;throw gt("mine",e)}}function Ur(e){if(qr(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(!ol(o))throw gt("status",e);n.includes(o)||n.push(o)}}return n.length>0?n:void 0}function hs(e,t,n,r){if(qr(t))return;let o=il(t);if(o===null||o<n||o>r)throw gt(e,t);return o}function Ln(e){let t={},n=al(En("mine",e.mine));n!==void 0&&(t.mine=n);let r=Ur(e.status);r&&(t.status=r);let o=En("flowId",e.flowId??e.flow_id??e.flow);typeof o=="string"&&o.length>0&&(t.flowId=o);let s=hs("limit",En("limit",e.limit),1,mt);s!==void 0&&(t.limit=s);let i=hs("offset",En("offset",e.offset),0,Number.MAX_SAFE_INTEGER);return i!==void 0&&(t.offset=i),t}function Ue(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 dl(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),mt);return{items:e.slice(r,r+o),total:n,limit:o,offset:r}}function ht(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||!ft(c,t)).filter(c=>Br(c.status,r.status)).map(c=>({...V(c,n),mine:ft(c,t)})),l=dl(d,r);return{proposals:l.items,total:l.total,limit:l.limit,offset:l.offset}}function _e(e,t,n,r={}){if(!n)throw new g("Missing proposal id","missing_proposal_id");let o=e.get(n);return{...V(o,r),mine:ft(o,t)}}function ll(e){let t={kind:"access-link",linkId:e.id};return typeof e.label=="string"&&e.label&&(t.label=e.label),t}function yt(e,t,n,r,o,s={}){if(!r)throw new g("Missing proposal id","missing_proposal_id");let i;try{i=e.get(r)}catch(f){throw f instanceof J,f}if(!ft(i,n))throw new at(r);if(i.status!=="pending")throw new G(r,i.status);let d=typeof o=="string"&&o.trim()?o.replace(/\0/g,"").trim().slice(0,2e3):"Withdrawn by access link",l=ll(n),c=e.markWithdrawn(r,l,d),p=V(c,s),u=p.flowLabel||void 0;return t.append("proposal.withdrawn",l,{proposalId:c.id,flowId:c.payload.flowId,title:p.title,displayName:p.displayName,reason:d,...u?{flowLabel:u}:{}}),{...p,mine:!0}}function cl(e,t,n,r={}){let o;try{o=e.get(t.id)}catch{return{}}if(r.liveRev){D(e,r.liveRev,{flowId:o.payload.flowId});try{o=e.get(t.id)}catch{return{}}}return o.status!=="pending"?{}:we(ye(e,o,n))}function ee(e,t,n,r={},o={}){let s="mine"in n?n:{...V(n,r),mine:ft(n,t)},i=0;if(o.liveRev)try{let c=e.get(s.id).payload.flowId;i=D(e,o.liveRev,{flowId:c});let p=e.get(s.id);s={...V(p,r),mine:ft(p,t)}}catch{}let d=Z(s,r),l={...d,mine:s.mine,...H(i)};return o.includeConflicts!==!1&&d.status==="pending"&&Object.assign(l,cl(e,d,t.id)),l}async function te(e){let t=await e.getFlows();return{rev:t.rev,flows:t.flows}}async function pe(e,t){if(!t)throw new g("Missing flow id","missing_flow_id");let n=await e.getFlow(t);return{rev:n.rev,flow:lt(t,n.flow)}}function pl(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 ws(e,t){if("nodes"in e&&e.nodes!==void 0&&!Array.isArray(e.nodes))throw new g("payload.nodes must be an array of node ops","invalid_nodes");if(t!==void 0&&!Array.isArray(t))throw new g("nodes must be an array of node ops","invalid_nodes")}async function ul(e,t,n,r,o){let{applyFlowNodeOps:s,interpretPatch:i}=await Promise.resolve().then(()=>(Rn(),gs));ws(n,r);let d=Array.isArray(n.nodes)?n.nodes:Array.isArray(r)?r:null;if(!d)throw new g("payload.flow is required","missing_flow");let l=n.flowId,c=typeof l=="string"?l:"";if(!c)throw new g("payload.flowId is required","missing_flow_id");let p=i({nodes:d,flowId:c},c);if(p.kind!=="nodes")throw new g("payload.nodes must be node ops","invalid_patch");let u=await s(e,p.flowId,p.ops,{expectedRev:t,...o?{nodeTypes:o}:{}});if(u.expectedRev!==t)throw new F(t,u.expectedRev,"submit_proposal");return{flowId:p.flowId,flow:u.flow}}async function fl(e,t,n,r,o){let s=n.flowId,i=n.flow,d=!i||typeof i!="object"||Array.isArray(i);if(d&&ws(n,r),d&&(Array.isArray(n.nodes)||Array.isArray(r))){let l=await ul(e,t,n,r,o);s=l.flowId,i=l.flow}if(typeof s!="string"||s.length===0)throw new g("payload.flowId is required","missing_flow_id");if(!i||typeof i!="object"||Array.isArray(i))throw new g("payload.flow is required","missing_flow");return{flowId:s,flow:i}}function ml(e,t){try{return{safeExpectedRev:Te(e.slice(0,200),"expectedRev"),safeFlowId:Te(t.slice(0,200),"flowId")}}catch{throw new g("expectedRev and payload.flowId must be safe path segments","invalid_path_segment")}}async function Ie(e,t,n,r,o,s={},i){let d=r.type,l=pl(r),c=r.payload;if(d!=="replace_flow")throw new g("type must be 'replace_flow'","invalid_proposal_type");if(!l)throw new g("expectedRev is required (top-level field next to type and payload)","missing_expected_rev");if(!c||typeof c!="object"||Array.isArray(c))throw new g("payload must be an object","invalid_payload");let u=await fl(o,l,c,r.nodes,i),{safeExpectedRev:f,safeFlowId:m}=ml(l,u.flowId),h=u.flow,y=Buffer.byteLength(JSON.stringify(c),"utf8");if(y>E)throw new M("proposal payload",y,E);Le(Zo(h,m));let k=it(h),re=await o.getFlows();if(re.rev!==f)throw new F(f,re.rev,"submit_proposal");Le(Qo(k,m,re.flows));let U=await o.getFlow(m);if(U.rev!==f)throw new F(f,U.rev,"submit_proposal");let L=it(U.flow);i&&await Vo(i,k);let{errors:It,warnings:et}=await es(k,{flowId:m,baseFlow:L,...i?{nodeTypes:i}:{}});Le(It);let W={kind:"access-link",linkId:n.id};n.label!==void 0&&(W.label=n.label);let oe=Pn(r.title)??Dr(L,k),uo=e.create({type:"replace_flow",expectedRev:f,payload:{flowId:m,flow:k},baseFlow:L,title:oe,createdBy:W,...et.length>0?{warnings:et}:{}}),on=V(uo,s),fo=on.flowLabel||void 0;return t.append("proposal.created",W,{proposalId:uo.id,flowId:m,expectedRev:f,title:on.title,displayName:on.displayName,...fo?{flowLabel:fo}:{},...et.length>0?{warningCount:et.length}:{}}),on}function _(e,t="rest"){return Vt(e,t)}var mt,ys,sl,g,O=C(()=>{"use strict";de();vn();dt();yn();$();be();Ir();Be();Ne();ce();a(ft,"isMineProposal");mt=200,ys=new Set(["pending","approved","rejected","failed","withdrawn","expired","superseded"]);a(Br,"matchesStatusFilter");a(ol,"isProposalStatus");sl={status:[...ys].join(", "),limit:`integer 1\u2013${mt}`,offset:"integer \u2265 0",mine:"true, false, 1, 0",flowId:"one flow tab id"};a(gt,"invalidFilterError");a(En,"singleQueryValue");a(il,"parseIntegerFilter");a(qr,"isUnset");a(al,"parseMineFilter");a(Ur,"parseStatusFilter");a(hs,"parseRangeFilter");a(Ln,"parseListProposalsQuery");a(Ue,"listPageFields");a(dl,"applyListPagination");a(ht,"listAccessibleProposals");a(_e,"getAccessibleProposal");a(ll,"accessLinkActor");a(yt,"withdrawMyProposal");a(cl,"conflictsForProposal");a(ee,"presentAccessibleProposalForApi");g=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(te,"listFlows");a(pe,"getFlow");a(pl,"resolveExpectedRev");a(ws,"assertNodesArrayOrAbsent");a(ul,"materializePayloadFromNodes");a(fl,"resolveReplaceFlowPayload");a(ml,"assertSafeRevAndFlowId");a(Ie,"submitReplaceFlowProposal");a(_,"aiOpsErrorToHttp")});function Gr(e){return e==="get_link"?86400:2592e3}function Wr(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 Ge,Xt=C(()=>{"use strict";a(Gr,"maxTtlSecondsForKind");a(Wr,"ttlCapErrorMessage");Ge="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 b(e,t,n,r,o=vt,s="error"){return{code:e,status:t,meaning:n,hint:r,channels:o,kind:s}}function Yt(e){return hl.get(e)?.hint}function bs(e){return e.replace(/\\/g,"\\\\").replace(/\|/g,"\\|")}function vs(){let e=a(s=>`| \`${s.code}\` | ${s.status} | ${bs(s.meaning)} | ${bs(s.hint)} | ${s.channels.join(", ")} |`,"row"),t=["| Code | HTTP | Meaning | Next step | Channels |","| --- | --- | --- | --- | --- |"],n=Cn.filter(s=>s.kind==="error"),r=Cn.filter(s=>s.kind==="warning"),o=Cn.filter(s=>s.kind==="status");return[...t,...n.map(e),"","**Warnings** (in `warnings[]` on a successful propose; never blocking):","",...t,...r.map(e),"","**Auto-closed proposal statuses.** Pending proposals are closed automatically: `expired` when the live flow rev moves past their `expectedRev`, `superseded` when another proposal on the same flow is approved. Submit, get, and list responses include `autoClosedCount` when that request auto-closed proposals (omitted when zero).","",...t,...o.map(e)].join(`
|
|
7
|
+
`)}var vt,wt,zr,gl,bt,Cn,hl,Nn=C(()=>{"use strict";Xt();vt=["GET","REST","MCP"],wt=["GET"],zr=["REST","MCP"],gl=["GET","REST"],bt=["admin"];a(b,"e");Cn=[b("access_link_missing",401,"No Bearer / path token","Ask the user for a new access link of the right kind"),b("bearer_scheme_required",401,"Auth used Basic (or non-Bearer)","Send `Authorization: Bearer <token>`",zr),b("token_expired",401,"Known token past `expiresAt` (body includes `expiredAt` and `accessLinksUrl`)","Ask the user to create a new link on the `/mcp` page \u2192 Access links (`accessLinksUrl`)"),b("access_link_invalid",401,"Unknown, revoked, or cleared after restart","Ask the user for a new access link"),b("wrong_token_kind",403,"Token kind does not match this surface","Mint the matching kind on `/mcp` \u2192 Access links; show `tellUser` to the human"),b("ai_features_disabled",403,"Plugin AI features off","Owner enables AI Access / `adminAuth`"),b("auth_required",401,"Admin session required","Sign in to the Node-RED editor",bt),b("insufficient_permissions",403,"Admin lacks required scope","Need `*` for link mint / approve",bt),b("invalid_link_kind",400,"Unknown `kind` on create","Use `get_link`, `rest`, or `mcp`",bt),b("invalid_ttl",400,"`ttlSeconds` is zero, negative, not a number, or over the cap for the kind",Ge,bt),b("access_link_limit",409,"Too many active links","Revoke an existing link first",bt),b("access_link_not_found",404,"Unknown link id on revoke","List links and use an existing id",bt),b("link_create_not_supported",403,"Attempted mint under a GET link","Admin mints via `POST /mcp/api/v1/links` only",wt),b("rate_limited",429,"Per-IP or per-token limit","Wait `retryAfter` / `Retry-After`"),b("payload_too_large",413,"Body / proposal over ~1.5MB","Shrink content"),b("invalid_json",400,"Malformed JSON body","Fix Content-Type / body",zr),b("unsupported_media_type",415,"POST body is not `application/json`","Send `Content-Type: application/json`",zr),b("url_too_long",414,"GET URL over soft cap (~8KB)","Use chunked drafts or REST/MCP",wt),b("not_found",404,"Unknown path or resource","Re-read docs / discovery"),b("missing_query",400,"Required search `q` missing","Pass `?q=`",gl),b("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"),b("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"]),b("invalid_diff_format",400,"Unknown `format` on proposal diff","Use `format=summary` (default) or `format=full`"),b("confirm_required",400,"GET withdraw without `confirm=1`","Repeat the request with `confirm=1`",wt),b("missing_expected_rev",400,"Proposal submitted without `expectedRev` / `rev`","Read the flow and pass its `rev`"),b("version_mismatch",409,"Stale `rev` / `expectedRev`","Re-read flows and resubmit"),b("proposal_not_found",404,"Unknown proposal id","List proposals and use an existing id"),b("proposal_not_pending",409,"Action needs a pending proposal","Only pending can withdraw / decide"),b("proposal_not_owned",403,"Withdraw of another token\u2019s proposal","Only the creating token can withdraw"),b("validation_failed",400,"Several validation errors at once (message `N validation errors`)","Fix every entry in errors[] and resubmit."),b("duplicate_node_id",400,"The same node id appears more than once in a full tab or batch","Give every node a unique id"),b("missing_node_id",400,"Node / op without an id","Include a non-empty string id"),b("missing_node_type",400,"Node without a `type`","Set `type` to an installed palette type name"),b("invalid_node_type",400,"`type` is not a string","Use the palette type name exactly"),b("invalid_nodes",400,"`flow.nodes` / `flow.configs` not an array","Send arrays (use `[]` to clear)"),b("invalid_node",400,"Node entry is not an object","Send `{ id, type, \u2026 }` objects"),b("invalid_wires",400,"Bad wires shape, missing target, or more ports than outputs","Wire only to existing node ids / ports"),b("z_mismatch",400,"Node `z` differs from the flow id","Set `z` to the tab id or omit it"),b("node_already_exists",400,"`add` op for an id already in the flow","Use `set` or a new id"),b("protected_property",400,"Tried to change id / type / z","Omit id, type, and z from edits"),b("node_not_found",404,"Unknown node (single error only; several errors \u2192 `validation_failed`)","Re-read the flow"),b("field_not_found",404,"Unknown field on a node","Re-read the node"),b("text_not_found",400,"`replace_text` find had no match","Re-read field; find is exact"),b("ambiguous_match",409,"`replace_text` matched more than once","Pass `occurrence` (1-based) or unique find"),b("unknown_node_type",400,"Proposal uses a type that is not installed","List node-types / palette search; ask user to install"),b("node_type_not_found",404,"Unknown type on node-types detail","List types or search palette"),b("chunk_conflict",409,"Same draft seq re-sent with different data","Re-send identical data or start a new draft",wt),b("missing_chunks",400,"Draft submit with gaps in seq","Upload every seq from 0 through max",wt),b("draft_hash_mismatch",400,"Draft sha256 does not match assembled bytes","Send the hex sha256 of the assembled UTF-8",wt),b("missing_config_node",201,"Warning: node has no (or an unresolved) config node yet","Configure before deploy if needed; see `candidates`",vt,"warning"),b("unwired_node",201,"Warning: node has outputs but no wires","Wire it, or ignore while scaffolding",vt,"warning"),b("incomplete_wires",201,"Warning: fewer wire ports than outputs","Add ports if outputs should connect",vt,"warning"),b("expired",200,"Status: pending proposal auto-closed because the live flow rev moved past `expectedRev`","Re-read the flow and submit a new proposal",vt,"status"),b("superseded",200,"Status: pending proposal auto-closed because another proposal on the same flow was approved","Re-read the flow and submit a new proposal if still needed",vt,"status"),b("upstream_error",502,"Node-RED runtime/flows error","Retry; check Node-RED logs"),b("internal_error",500,"Unexpected server error","Retry; check Node-RED logs")],hl=new Map(Cn.map(e=>[e.code,e]));a(Yt,"registryHint");a(bs,"cell");a(vs,"renderGetLinkErrorTable")});function X(e,t,n={}){return Jr(e,t,n)}function yl(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 wl(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 Fe(e,t,n,r){return e==="mcp"?t:e==="rest"?n:r}function Jr(e,t,n={}){switch(e){case"version_mismatch":return Fe(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 Fe(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 Fe(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 Fe(t,"Call list_proposals and use an id from that response.","List proposals and use an id from that response.","List proposals and use an id from that response.");case"proposal_not_owned":return"Only the access link that created a pending proposal can withdraw it.";case"proposal_not_pending":return"Only pending proposals can be withdrawn or decided.";case"confirm_required":return"Add confirm=1 to the withdraw URL (required so link prefetchers cannot withdraw).";case"invalid_wires":return yl(t,n.cause);case"node_type_not_found":case"unknown_node_type":return wl(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 Fe(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 Fe(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 Fe(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 Fe(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 Vt(e,t,n={}){let r=n.canonicalizeMessage!==!1,o=null;if(e instanceof g?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,...e.details??{}}}:e instanceof F?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,expectedRev:e.expectedRev,currentRev:e.currentRev}}:e instanceof I?o={statusCode:e.statusCode,body:{error:e.message,code:e.code,flowId:e.flowId}}:e instanceof J||e instanceof at||e instanceof G||e instanceof M?o={statusCode:e.statusCode,body:{error:e.message,code:e.code}}:(e instanceof he||e instanceof ae)&&(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=ve[s];i&&(o.body.error=i)}if(typeof o.body.hint!="string"||!o.body.hint){let i=Jr(s,t,{cause:o.body.cause})??Yt(s);i&&(o.body.hint=i)}return o}function ks(e,t,n={}){let o={error:n.error??ve[t]??"Request failed",code:t,...n.details??{}},s=Jr(t,e,{cause:o.cause})??Yt(t);return s&&(o.hint=s),{statusCode:n.statusCode??400,body:o}}var ve,be=C(()=>{"use strict";de();dt();$();O();Nn();ve={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(X,"channelErrorHint");a(yl,"invalidWiresHint");a(wl,"nodeTypeHint");a(Fe,"pickChannel");a(Jr,"hintFor");a(Vt,"mapErrorForChannel");a(ks,"buildChannelError")});var _u={};mo(_u,{ACCESS_LINK_INVALID_MESSAGE:()=>Me,ACCESS_LINK_MISSING_MESSAGE:()=>Q,AccessLinkStore:()=>Qe,EDITOR_SIDEBAR_ASSET_VERSION:()=>xt,MAX_PROPOSAL_JSON_BYTES:()=>E,SettingsStore:()=>nt,areAiFeaturesEnabled:()=>ie,bindSettingsStore:()=>un,createPluginServices:()=>cr,default:()=>Tu,getStorageDir:()=>nr,isAdminAuthEnabled:()=>Y,packageVersion:()=>St,registerRoutes:()=>oa,renderEditorSidebarJs:()=>In,renderEditorSidebarLoaderHtml:()=>Rs,requireAccessLink:()=>st,resolveAiAccessSettings:()=>se});module.exports=pa(_u);$e();Se();function No(e){let t=z(e);return`auth-tokens${(t==="/"?"":t).replace(/\//g,"-")}`}a(No,"editorAuthTokenKey");Se();be();function bl(e){if(!e)return!1;let t=e.split(";")[0]?.trim().toLowerCase()??"";return t==="application/json"||t.endsWith("+json")}a(bl,"isJsonContentType");function vl(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(vl,"hasRequestBody");function kt(e="rest"){return(t,n,r)=>{let o=t.headers["content-type"];if(bl(o)){r();return}if(!o&&!vl(t)){r();return}let s=ks(e,"unsupported_media_type",{statusCode:415});n.status(s.statusCode).json(s.body)}}a(kt,"requireJsonContentType");var kl="text/plain; charset=utf-8",Al="text/html; charset=utf-8",Sl="default-src 'none'; base-uri 'self'; form-action 'none'; frame-ancestors 'none'";function xl(e){let t=e.query?.format;return typeof t=="string"?t.toLowerCase():void 0}a(xl,"formatParam");function Tl(e){let t=xl(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(Tl,"chooseGetLinkRepresentation");function As(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}a(As,"escapeHtml");var _l=/(?<![A-Za-z0-9_.~\-/])\.\/[A-Za-z0-9._~\-/]+(?:\?[A-Za-z0-9._~\-=&%]+)?/g;function Pl(e){return e.replace(_l,(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(Pl,"linkifyEscaped");function Rl(e,t){return`<!doctype html>
|
|
8
|
+
<html><head><meta charset="utf-8">${t?`<base href="${As(t)}">`:""}<title>Node-RED AI Access</title></head><body><pre>${Pl(As(e))}</pre></body></html>
|
|
9
|
+
`}a(Rl,"renderGetLinkHtml");function El(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(El,"getLinkBaseHref");function Ll(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}a(Ll,"prettyJson");function Ss(){return(e,t,n)=>{let r=Tl(e),o=El(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 p=typeof i=="string"?i:Buffer.isBuffer(i)?i.toString("utf8"):void 0;return p===void 0?s(i):r==="plain"?(t.setHeader("Content-Type",kl),s(p)):(t.setHeader("Content-Type",Al),t.setHeader("Content-Security-Policy",Sl),s(Rl(l?Ll(p):p,o)))}),n()}}a(Ss,"getLinkContentNegotiation");var Cl=/^\/mcp\/(?:a(?:\/|$)|api(?:\/|$)|rpc\/?$)/,Nl="GET, POST, DELETE, OPTIONS",Il="Authorization, Content-Type, Accept, Mcp-Session-Id, Mcp-Protocol-Version, Last-Event-ID",Fl="Mcp-Session-Id, WWW-Authenticate, Retry-After, RateLimit, RateLimit-Policy, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset";function xs(e){let t=Array.isArray(e)?e:typeof e=="string"?[e]:[],n=new Set;for(let r of t)if(!(typeof r!="string"||r.trim().length===0))try{let o=new URL(r.trim());(o.protocol==="http:"||o.protocol==="https:")&&n.add(o.origin.toLowerCase())}catch{}return[...n]}a(xs,"normalizeCorsOrigins");function Ol(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()}a(Ol,"canonicalOrigin");function Dl(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(", "))}a(Dl,"appendVary");function Ts(e){return(t,n,r)=>{if(!Cl.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,p)=>!s&&/^access-control-/i.test(c)?n:o(c,p));let i=Ol(t.get("origin")),d=i===void 0?void 0:e().find(c=>c===i),l=d!==void 0;if(Dl(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",Fl),t.method==="OPTIONS"&&t.get("access-control-request-method")){n.setHeader("Access-Control-Allow-Methods",Nl),n.setHeader("Access-Control-Allow-Headers",Il),n.setHeader("Access-Control-Max-Age",String(600)),n.status(204).end();return}s=!1,r()}}a(Ts,"aiCorsMiddleware");function _s(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)}a(_s,"installAiCorsMiddleware");Se();function Ml(e){let t=me(e.settings.httpAdminRoot),n=Pe(e.settings.httpAdminRoot),r="0.1.1",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 /mcp Access links page (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(Ml,"buildDiscoveryIndex");function Kr(e){return(t,n)=>{n.status(200).json(Ml(e))}}a(Kr,"discoveryIndexHandler");function Ps(){return(e,t)=>{t.status(404).json({error:"Not found",code:"not_found"})}}a(Ps,"mcpApiNotFoundHandler");var At;function St(){return At||(At="0.1.1",At)}a(St,"packageVersion");var xt=St();function $l(){return`#red-ui-sidebar-ai-access {
|
|
8
10
|
height: 100%;
|
|
9
11
|
display: flex;
|
|
10
12
|
flex-direction: column;
|
|
@@ -141,7 +143,7 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
|
|
|
141
143
|
border: 0;
|
|
142
144
|
background: transparent;
|
|
143
145
|
color: transparent;
|
|
144
|
-
}`}a(
|
|
146
|
+
}`}a($l,"editorSidebarStyles");function jl(){return`
|
|
145
147
|
/**
|
|
146
148
|
* Editor right-hand sidebar tab: AI Access.
|
|
147
149
|
* Served from /mcp/editor/ai-access-sidebar.js (no-store + ?v= cache-bust).
|
|
@@ -409,7 +411,7 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
|
|
|
409
411
|
saveSettings({ silent: true });
|
|
410
412
|
}, 400);
|
|
411
413
|
}
|
|
412
|
-
`}a(
|
|
414
|
+
`}a(jl,"editorSidebarScriptPartA");function Hl(){return`
|
|
413
415
|
function buildContent() {
|
|
414
416
|
var content = $('<div></div>', { id: CONTENT_ID, class: 'red-ui-sidebar-ai-access' });
|
|
415
417
|
|
|
@@ -537,8 +539,8 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
|
|
|
537
539
|
},
|
|
538
540
|
});
|
|
539
541
|
})();
|
|
540
|
-
`}a(
|
|
541
|
-
`)}a(
|
|
542
|
+
`}a(Hl,"editorSidebarScriptPartB");function Bl(){return jl()+Hl()}a(Bl,"editorSidebarScriptBody");function In(){let e=JSON.stringify($l());return[`/* @com55/node-red-ai-access editor sidebar v=${xt} */`,"(function(){"," if (typeof document === 'undefined') return;"," if (!document.getElementById('com55-ai-access-sidebar-css')) {"," var style = document.createElement('style');"," style.id = 'com55-ai-access-sidebar-css';",` style.textContent = ${e};`," document.head.appendChild(style);"," }","})();",Bl()].join(`
|
|
543
|
+
`)}a(In,"renderEditorSidebarJs");function Rs(e=xt){return`<script>
|
|
542
544
|
/**
|
|
543
545
|
* Thin loader for the AI Access editor sidebar.
|
|
544
546
|
* Logic lives at GET /mcp/editor/ai-access-sidebar.js (Cache-Control: no-store)
|
|
@@ -572,8 +574,8 @@ ${t}`))return{text:t,summary:t};let r=t.split(/\n(?=#{1,3}\s+)/),o="",s,i;for(le
|
|
|
572
574
|
(document.head || document.documentElement).appendChild(s);
|
|
573
575
|
})();
|
|
574
576
|
</script>
|
|
575
|
-
`}a(
|
|
576
|
-
\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(
|
|
577
|
+
`}a(Rs,"renderEditorSidebarLoaderHtml");O();be();function Es(e){return ve[e]??"Request failed"}a(Es,"safeErrorText");function ql(e,t){let n=_(t,"rest");if(!n)return!1;let r=n.body.code,o={error:Es(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(ql,"sendMappedFlowError");function Ls(e){return async(t,n)=>{try{n.json(await te(e))}catch(r){if(ql(n,r))return;n.setHeader("X-Content-Type-Options","nosniff"),n.status(502).json({error:Es("upstream_error"),code:"upstream_error"})}}}a(Ls,"listFlowsHandler");xe();O();Se();be();function Tt(e){return e==null||e===""?{ok:!0,format:"summary"}:e==="summary"||e==="full"?{ok:!0,format:e}:{ok:!1}}a(Tt,"resolveDiffFormat");function Ul(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 p=l;p<=c;p+=1)n[p]=!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(Ul,"compactLineDiff");function Gl(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=Ul(e.lines),d={key:e.key,kind:"text",beforeBytes:o,afterBytes:s,lines:i.lines};return i.truncated&&(d.truncated=!0),d}a(Gl,"formatTextChange");function Wl(e,t){return e.map(n=>n.kind==="text"?Gl(n,t):n)}a(Wl,"formatPropertyChanges");function Cs(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)}
|
|
578
|
+
\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(Cs,"formatPresenceText");function Ns(e,t){return{summary:e.summary,added:e.added.map(n=>Cs(n,t)),removed:e.removed.map(n=>Cs(n,t)),modified:e.modified.map(n=>({...n,changes:Wl(n.changes,t)}))}}a(Ns,"formatSections");function _t(e,t="summary"){let n=Ns(e,t),r={...e,...n,format:t};return e.vsCurrent&&(r.vsCurrent=Ns(e.vsCurrent,t)),r}a(_t,"applyDiffFormat");$();xe();Nn();function Pt(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}a(Pt,"escapeForDocs");var Is="https://YOUR-NODE-RED/mcp/rpc";function zl(e,t,n,r=Is){return`## Also available (prefer when your client can send headers)
|
|
577
579
|
|
|
578
580
|
**Prefer MCP or REST** whenever the AI client can send custom headers and JSON
|
|
579
581
|
bodies \u2014 no URL length limits and no chunked drafts. Use this **GET link
|
|
@@ -605,7 +607,7 @@ Use an **mcp** token from the owner (not the get_link path token).
|
|
|
605
607
|
{
|
|
606
608
|
"mcpServers": {
|
|
607
609
|
"node-red-ai-access": {
|
|
608
|
-
"url": "
|
|
610
|
+
"url": "${r}",
|
|
609
611
|
"headers": {
|
|
610
612
|
"Authorization": "Bearer aia_\u2026"
|
|
611
613
|
}
|
|
@@ -620,7 +622,7 @@ Use an **mcp** token from the owner (not the get_link path token).
|
|
|
620
622
|
{
|
|
621
623
|
"mcpServers": {
|
|
622
624
|
"node-red-ai-access": {
|
|
623
|
-
"url": "
|
|
625
|
+
"url": "${r}",
|
|
624
626
|
"headers": {
|
|
625
627
|
"Authorization": "Bearer aia_\u2026"
|
|
626
628
|
}
|
|
@@ -630,19 +632,19 @@ Use an **mcp** token from the owner (not the get_link path token).
|
|
|
630
632
|
\`\`\`
|
|
631
633
|
|
|
632
634
|
**ChatGPT connectors / custom GPT:** use Streamable HTTP URL
|
|
633
|
-
\`
|
|
635
|
+
\`${r}\` and set Authorization Bearer \`aia_\u2026\`
|
|
634
636
|
(an **mcp** token).
|
|
635
637
|
|
|
636
638
|
**Generic Streamable HTTP** (curl):
|
|
637
639
|
|
|
638
640
|
\`\`\`bash
|
|
639
|
-
curl -sS -X POST "
|
|
641
|
+
curl -sS -X POST "${r}" \\
|
|
640
642
|
-H "Authorization: Bearer aia_\u2026" \\
|
|
641
643
|
-H "Content-Type: application/json" \\
|
|
642
644
|
-H "Accept: application/json, text/event-stream" \\
|
|
643
645
|
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
|
|
644
646
|
\`\`\`
|
|
645
|
-
`}a(
|
|
647
|
+
`}a(zl,"docsAlsoAvailable");function Jl(e,t){return`# Node-RED AI Access \u2014 self-contained GET link (get_link)${e}
|
|
646
648
|
|
|
647
649
|
**Git mental model:** a proposal is like a pull request. Human approve = merge;
|
|
648
650
|
reject = close; withdraw = you close your own PR. If \`rev\`/\`expectedRev\` does
|
|
@@ -660,6 +662,15 @@ A **human owner** must approve every flow change before it is deployed.
|
|
|
660
662
|
All paths below are **relative to this link URL** (the address you used to open
|
|
661
663
|
these docs).
|
|
662
664
|
|
|
665
|
+
## Response content type
|
|
666
|
+
|
|
667
|
+
Responses on GET links default to HTML so web fetchers used by AI chat apps
|
|
668
|
+
accept them: a minimal page with the same text escaped in one \`<pre>\`.
|
|
669
|
+
Request the raw type via Accept or ?format= (\`Accept: text/markdown\` or
|
|
670
|
+
\`?format=md\` for this guide; \`Accept: application/json\` or \`?format=json\`
|
|
671
|
+
for JSON; \`Accept: text/plain\` for raw text). Status codes and data are the
|
|
672
|
+
same either way.
|
|
673
|
+
|
|
663
674
|
## Expiry
|
|
664
675
|
|
|
665
676
|
- Link expires at: **${t}** (ISO-8601); \`GET ./status\` also reports
|
|
@@ -680,7 +691,7 @@ these docs).
|
|
|
680
691
|
- **Create / mint tokens** \u2014 this GET link **cannot** create access tokens.
|
|
681
692
|
There is no \`./links\` create operation under \`/mcp/a/<token>\`. Only a human
|
|
682
693
|
admin with \`*\` may mint tokens via \`POST /mcp/api/v1/links\` on the \`/mcp\`
|
|
683
|
-
Access links page. Attempts under this URL return \`
|
|
694
|
+
Access links page. Attempts under this URL return \`403 link_create_not_supported\`.
|
|
684
695
|
- Approve or reject proposals (human on \`/mcp\` only).
|
|
685
696
|
- Use this path token as a REST or MCP Bearer (wrong kind \u2014 need separate tokens).
|
|
686
697
|
|
|
@@ -708,7 +719,7 @@ fields at the top level** (no \`ok\` / \`proposal\` wrapper). Read
|
|
|
708
719
|
|
|
709
720
|
Lean by default (no \`payload\` / \`baseFlow\`). Pass
|
|
710
721
|
\`?include=payload,baseFlow\` or \`?include_payload=true\` to include them.
|
|
711
|
-
`}a(
|
|
722
|
+
`}a(Jl,"docsIntro");function Kl(){return"## Step-by-step workflow\n\n1. `GET ./status` \u2014 confirm the link works; note `caller.expiresAt` /\n `caller.minutesRemaining`.\n2. `GET ./flows` \u2014 list tabs + current `rev`.\n3. **Layered read (prefer this before loading whole tabs):**\n - `GET ./flows/<id>?summary=1` \u2014 node list with `id`/`type`/`name`/`wires` +\n per-node `bytes` (no field contents).\n - `GET ./search?q=<text>&flow=<id>` \u2014 hits with node id, field, line numbers,\n short snippet (`truncated:true` when the result cap is hit).\n - `GET ./nodes/<id>` \u2014 one full node (`flowId` included when known).\n - `GET ./nodes/<id>/field/<name>?lines=A-B` \u2014 one field (optional 1-based\n line range) plus `totalLines` and `bytes`.\n4. Prefer a **targeted text replace** for small edits inside long string fields\n (`template`, `func`, \u2026) \u2014 unique `find` (or `occurrence`) keeps the diff short:\n\n```\nGET ./propose?flow=<flowId>&rev=<rev>&node=<nodeId>&field=<prop>&find=<text>&replace=<text>&occurrence=<n>&title=<text>\n```\n\n5. Or set whole properties (optional `title` \u226480 chars; `summary` is a title fallback):\n\n```\nGET ./propose?flow=<flowId>&rev=<rev>&node=<nodeId>&set.<prop>=<urlencoded>&title=<text>\n```\n\n6. Or multi-node / add / remove / `replace_text` / full tab via base64url JSON patch:\n\n```\nGET ./propose?flow=<flowId>&rev=<rev>&patch=<base64url-json>&title=<text>\n```\n\n7. Poll `GET ./proposals` / `./proposals/<id>` until approved, rejected, or\n withdrawn. Every token can list/read **all** proposals; each item has\n `mine:true|false`. Filter with `?mine=true` (own) or `?mine=false` (others'),\n `?status=pending,superseded` (comma-separated or repeated), `?flowId=`;\n paginate with `?limit=` (1\u2013200) / `?offset=` (response includes `total`,\n `offset`, `hasMore`, and `nextOffset` when more remain). Invalid filters\n return `400 invalid_filter` naming the parameter. Cite `displayName` +\n `reviewUrl` when updating the user.\n8. Optional: `GET ./proposals/<id>/diff` (default `format=summary` compact\n line hunks + byte sizes; `format=full` for complete text) against the\n **base snapshot** stored at submit. Added/removed nodes are presence blocks\n with a summary sentence; pending+stale may include `vsCurrent`.\n9. Withdraw your own pending proposal:\n `GET ./proposals/<id>/withdraw?confirm=1&reason=<text>` (status becomes `withdrawn`,\n not `rejected`). Only the link that created a proposal can withdraw it;\n others get `403 proposal_not_owned`.\n\n**Recommended edit loop:** summary \u2192 search \u2192 read a line range \u2192\n`replace_text` \u2192 propose.\n\nOn submit and on read of a pending proposal, the response may include\n`conflictingProposals` (same-rev or overlapping pending work; list omits this) with\n`overlappingNodeIds` / `overlappingFields` when detectable \u2014 treat like a\ngit conflict warning; prefer one pending change per flow when possible.\n\n## Node types and palette (read-only)\n\nBefore proposing unfamiliar nodes:\n\n1. `GET ./node-types?q=<text>` \u2014 list installed palette types (filter optional;\n results capped; may include `truncated` / `total`).\n2. `GET ./node-types/<type>` \u2014 detail (`defaults` as JSON, `inputs`/`outputs`,\n `requiredConfigNodeTypes`, help sections). Pass `?help=0` to omit help.\n Types are **case-sensitive** (`Debug` \u2192 did you mean `debug`?).\n3. `GET ./palette/search?q=<text>` \u2014 search the public Node-RED catalogue.\n **Recommend only** \u2014 ask the user to install via **Manage palette**, then\n recheck `./node-types`. This API **never installs** packages.\n Core `node-red` types are **not** in the catalogue \u2014 use `./node-types`.\n\nSubmit/propose with an unknown `node.type` returns `400 unknown_node_type`\nwith `nearest` suggestions \u2014 call node-types / palette search and ask the user\nto install the module, then retry.\n"}a(Kl,"docsWorkflow");function Vl(){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(Vl,"docsUrlTable");function Xl(){return`## Propose examples
|
|
712
723
|
|
|
713
724
|
**Recommended for small edits to long fields** \u2014 \`replace_text\` (find must be
|
|
714
725
|
**unique** unless you pass \`occurrence\` as a 1-based index). Creates a normal
|
|
@@ -786,7 +797,7 @@ GET ./propose?flow=tab1&rev=abc123&patch=<base64url>
|
|
|
786
797
|
Node-level proposes create a normal \`replace_flow\` proposal; owners should
|
|
787
798
|
approve with deployment type **\`nodes\`** (plugin default) so only changed
|
|
788
799
|
nodes deploy.
|
|
789
|
-
`}a(
|
|
800
|
+
`}a(Xl,"docsProposeExamples");function Yl(){return`## Chunked drafts (long HTML / large fields)
|
|
790
801
|
|
|
791
802
|
When the GET URL would exceed **${ot} bytes**, do not
|
|
792
803
|
stuff everything into \`propose\`. Use drafts (expire after **30 minutes**,
|
|
@@ -835,7 +846,7 @@ Or a full \`replace_flow\` document. JSON-mode drafts **may add nodes**, so
|
|
|
835
846
|
\`draft/new\` does not require an existing \`node\`/\`field\`. Use JSON mode for
|
|
836
847
|
structural changes; use field-mode only when you need to replace one whole
|
|
837
848
|
string field that is too large for a single propose URL.
|
|
838
|
-
`}a(
|
|
849
|
+
`}a(Yl,"docsDrafts");function Ql(){return`## Last resort: content larger than chunked drafts
|
|
839
850
|
|
|
840
851
|
If the field is still too large for chunked drafts (decoded size over the
|
|
841
852
|
proposal cap), use this fallback only after drafts fail:
|
|
@@ -849,7 +860,7 @@ proposal cap), use this fallback only after drafts fail:
|
|
|
849
860
|
**Trade-off:** pasted content is **not** reviewed through the proposal diff.
|
|
850
861
|
Prefer \`replace_text\` or chunked drafts whenever they fit so the human can
|
|
851
862
|
still see a readable change on \`/mcp\`.
|
|
852
|
-
`}a(
|
|
863
|
+
`}a(Ql,"docsLastResortPaste");function Zl(){return`## Size cap and REST/MCP nodes payload shapes
|
|
853
864
|
|
|
854
865
|
The proposal JSON cap is **${E} bytes**
|
|
855
866
|
(\`MAX_PROPOSAL_JSON_BYTES\` / ~1.5MB) on **every** channel: GET propose/drafts,
|
|
@@ -895,7 +906,7 @@ Example (canonical):
|
|
|
895
906
|
**Alternative (also accepted):** top-level \`nodes\` array as an alias for
|
|
896
907
|
\`payload.nodes\` (same op shapes). Full-tab replace remains supported via
|
|
897
908
|
\`payload.flow\` (from \`GET ./flows/<id>\` / REST \`GET /flows/{id}\`).
|
|
898
|
-
`}a(
|
|
909
|
+
`}a(Zl,"docsRestNodesPayload");function ec(){return`## Known issues
|
|
899
910
|
|
|
900
911
|
- **Rate limits are per token (and per IP).** Each access-link token has its own
|
|
901
912
|
token bucket. Several tokens can each use the full per-token limit at the same
|
|
@@ -903,20 +914,20 @@ Example (canonical):
|
|
|
903
914
|
- **\`RateLimit-*\` headers describe the per-token bucket** (default
|
|
904
915
|
\`RateLimit-Policy: 300;w=60\`) on token requests; requests without a token
|
|
905
916
|
report the per-IP bucket (\`600;w=60\`). Both are enforced.
|
|
906
|
-
`}a(
|
|
917
|
+
`}a(ec,"docsKnownIssues");function tc(){return`## Error codes (JSON unless docs)
|
|
907
918
|
|
|
908
|
-
${
|
|
919
|
+
${vs()}
|
|
909
920
|
|
|
910
921
|
All JSON responses use \`Cache-Control: no-store\`. Never put secrets in query
|
|
911
922
|
strings you will paste publicly beyond this temporary link.
|
|
912
|
-
`}a(
|
|
913
|
-
`)}a(ks,"renderGetLinkDocs");var Cs=require("crypto");$();D();be();Ce();ce();D();function Dn(e){return{error:e instanceof Error&&e.message.trim()?e.message:"Internal server error",code:"internal_error"}}a(Dn,"internalErrorBody");function xl(e,t){e.headersSent||e.status(500).json(Dn(t))}a(xl,"sendInternalError");function _(e,t){if(e.headersSent)return;let n=P(t);if(n){e.status(n.statusCode).json(n.body);return}xl(e,t)}a(_,"sendCaughtRouteError");function v(e){return(t,n,r)=>{Promise.resolve().then(()=>e(t,n,r)).catch(o=>{if(n.headersSent){r(o instanceof Error?o:new Error(String(o)));return}_(n,o)})}}a(v,"asyncRoute");var Mn=require("crypto");$();$e();var R=class extends Error{static{a(this,"DraftValidationError")}statusCode=400;code;constructor(t,n="draft_invalid"){super(t),this.name="DraftValidationError",this.code=n}},We=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"}},ze=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}},Tl=/^[A-Za-z0-9_-]+={0,2}$/;function As(e){if(!Tl.test(e)||e.replace(/=+$/,"").length===0)throw new R("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 R("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data")}if(t.byteLength===0&&e.length>0)throw new R("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data");return t}a(As,"assertValidBase64UrlChunk");var On=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,Mn.randomBytes)(12).toString("hex")),this.maxBytes=t.maxBytes??E,this.ttlMs=t.ttlMs??xo}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 ze(t,o.proposalId);let s=this.drafts.get(t);if(s?.linkId!==n)throw new We(t);if(s.expiresAtMs<=r)throw new We(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 R("seq must be a non-negative integer","invalid_seq");if(typeof o!="string")throw new R("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 R(`Chunk seq ${r} already stored with different data`,"chunk_conflict")}else{let p=As(o),u=s.totalBytes+p.byteLength;if(u>this.maxBytes)throw new R(`Draft exceeds limit of ${this.maxBytes} bytes (decoded)`,"payload_too_large");s.chunks.set(r,o),s.totalBytes=u}let d=[...s.chunks.keys()].sort((p,u)=>p-u),l=[],c=d.at(-1);if(c!==void 0)for(let p=0;p<=c;p+=1)s.chunks.has(p)||l.push(p);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 R("Draft has no chunks","draft_empty");let i=[...o.chunks.keys()].sort((m,h)=>m-h).at(-1);if(i===void 0)throw new R("Draft has no chunks","draft_empty");let d=[];for(let m=0;m<=i;m+=1)o.chunks.has(m)||d.push(m);if(d.length>0)throw new R(`Missing chunk seqs: ${d.join(",")}`,"missing_chunks");let l=[];for(let m=0;m<=i;m+=1){let h=o.chunks.get(m);if(h===void 0)throw new R(`Missing chunk seqs: ${m}`,"missing_chunks");l.push(h)}let c=l.map(m=>As(m)),p=Buffer.concat(c);if(p.byteLength>this.maxBytes)throw new R(`Assembled draft exceeds limit of ${this.maxBytes} bytes`,"payload_too_large");let u=r.trim().toLowerCase();if(!/^[0-9a-f]{64}$/.test(u))throw new R("invalid_sha256: expected 64 hex chars","invalid_sha256");if((0,Mn.createHash)("sha256").update(p).digest("hex")!==u)throw new R("sha256 does not match assembled draft content","draft_hash_mismatch");return{draft:o,assembled:p.toString("utf8")}}size(){return this.purgeExpired(this.now()),this.drafts.size}clear(){this.drafts.clear(),this.submitted.clear()}};En();Me();$e();var $n=Yi(require("express-rate-limit"),1);var Kr=6e4,Ss=600,xs=300;function Jr(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?Math.floor(e):t}a(Jr,"positiveInt");function Vr(e){if(!e)return{windowMs:Kr,maxPerIp:Ss,maxPerToken:xs};let t=e;return{windowMs:Jr(t.windowMs??t.rateLimitWindowMs,Kr),maxPerIp:Jr(t.maxPerIp??t.rateLimitMaxPerIp,Ss),maxPerToken:Jr(t.maxPerToken??t.rateLimitMaxPerToken,xs)}}a(Vr,"resolveRateLimitConfig");function Pl(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(Pl,"retryAfterSeconds");function _l(e,t,n=Kr){let r=Pl(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(_l,"sendRateLimited");function Rl(e){return(t,n,r,o)=>{_l(t,n,e)}}a(Rl,"rateLimitedHandler");function Ps(e){return`ip:${(0,$n.ipKeyGenerator)(e.ip??"unknown")}`}a(Ps,"clientIpKey");function Ts(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(Ts,"accessTokenRateLimitKey");function _s(e,t,n,r){let o={windowMs:t,max:e,standardHeaders:!0,legacyHeaders:!1,keyGenerator:n,handler:Rl(t)};return r&&(o.skip=r),(0,$n.default)(o)}a(_s,"createLimiter");function El(e){let t=Vr(e);return _s(t.maxPerIp,t.windowMs,Ps)}a(El,"createAiAccessRateLimiter");function Ll(e){let t=Vr(e);return _s(t.maxPerToken,t.windowMs,n=>Ts(n)??Ps(n),n=>Ts(n)==null)}a(Ll,"createAiAccessTokenRateLimiter");function jn(e){let t=Vr(e),n=El(t),r=Ll(t);return{ip:n,token:r,stack:[n,r],config:t}}a(jn,"createAiAccessDualRateLimiters");Ae();function T(e){e.setHeader("Cache-Control","no-store"),e.setHeader("Pragma","no-cache")}a(T,"setNoStore");function Xr(e){let t=e.originalUrl||e.url||"",n=hr(t);n!==t&&(Object.defineProperty(e,"originalUrl",{value:n,writable:!0,configurable:!0}),typeof e.url=="string"&&Object.defineProperty(e,"url",{value:hr(e.url),writable:!0,configurable:!0}))}a(Xr,"redactTokenFromRequestUrl");function Rs(e,t){return(n,r,o)=>{T(r);let s=n.originalUrl||n.url||"";if(n.getLinkUrlBytes=Buffer.byteLength(s,"utf8"),Xr(n),!ie(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",j),r.status(401).json({error:Q,code:ge,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"){$t(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",j),r.status(401).json({error:Oe,code:Mt,hint:c});return}if(l.link.kind!=="get_link"){jt(r,"get_link",l.link.kind);return}n.accessLink=l.link,n.getLinkToken=d,o()}}a(Rs,"requireGetLinkToken");function _t(e,t){let n=e.getLinkUrlBytes;return(typeof n=="number"?n:Buffer.byteLength(e.originalUrl||e.url||"","utf8"))>ot?(T(t),t.status(414).json({error:`Request URL exceeds ${ot} 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:ot}),!0):!1}a(_t,"rejectOversizedGetUrl");function S(e,t,n,r,o,s){T(e),e.status(t).json({error:n,code:r,...o?{hint:o}:{},...s})}a(S,"jsonError");function Es(){return(e,t)=>{Xr(e),T(t),t.status(404).json({error:"Not found",code:"not_found"})}}a(Es,"getLinkNotFoundHandler");var Cl="link_create_not_supported";function Ls(){return(e,t)=>{Xr(e),T(t),t.status(405).json({error:"GET access links cannot create tokens",code:Cl,hint:"Ask an admin with * permission to mint tokens via POST /mcp/api/v1/links on the /mcp Access links page. This get_link URL cannot create, list, or revoke tokens."})}}a(Ls,"getLinkMintNotSupportedHandler");function A(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(A,"queryString");function ue(e){switch(e){case"draft_not_found":return"Start a new draft with GET \u2026/draft/new.";case"draft_already_submitted":return"This draft already created a proposal; poll that proposal or start a new draft.";case"chunk_conflict":return"Re-send the same data for that seq, or start a new draft.";case"payload_too_large":return"Shrink the content; totalBytes counts decoded bytes against maxBytes.";case"invalid_seq":return"seq must be a non-negative integer (0, 1, 2, \u2026).";case"invalid_chunk":case"invalid_chunk_data":return"data must be base64url of the raw chunk bytes (A-Z, a-z, 0-9, -, _).";case"missing_chunks":return"Upload every seq from 0 through max before submit.";case"invalid_sha256":return"sha256 must be exactly 64 hexadecimal characters.";case"draft_hash_mismatch":return"sha256 must be the hex digest of the assembled UTF-8 bytes (before base64url).";case"draft_empty":return"Append at least one chunk before submit.";case"node_not_found":return"Call GET \u2026/flows/<id> and use a node id from that response (or use mode=json with op:add).";case"field_not_found":return"Use a property that exists on the node, or mode=json to add/replace nodes.";case"missing_draft_params":return"Example: \u2026/draft/new?flow=tab1&rev=abc&node=n1&field=template";case"version_mismatch":return"Re-read the flow and start a new draft with the current rev.";case"protected_property":return"Omit id, type, and z from set props. wires are allowed when targets exist in the flow.";case"invalid_wires":return"wires must list existing node ids (or add those nodes in the same patch).";default:return}}a(ue,"draftErrorHint");async function Nl(e,t,n){let r;try{r=await e.getFlow(t)}catch(s){let i=P(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(Nl,"validateFieldDraftTargets");function Il(e,t,n,r){if(r)try{let o=zt(r);if(!Array.isArray(o)||o.length===0)throw new Error("targets must be a non-empty array");return{ok:!0,mode:"field",targets:o.map(i=>{if(!i||typeof i!="object")throw new Error("bad target");let d=i,l=typeof d.nodeId=="string"?d.nodeId:typeof d.node=="string"?d.node:"",c=typeof d.field=="string"?d.field:"";if(!l||!c)throw new Error("bad target");return{nodeId:l,field:c}})}}catch{return{ok:!1,status:400,error:"targets must be base64url JSON array of {nodeId,field}",code:"invalid_targets"}}if(n==="json"||!e&&!t)return{ok:!0,mode:"json"};if(!e||!t){let o=ue("missing_draft_params");return{ok:!1,status:400,error:"node and field are required for field drafts (or pass mode=json)",code:"missing_draft_params",...o?{hint:o}:{}}}return{ok:!0,mode:"field"}}a(Il,"planDraftMode");async function Fl(e,t,n,r){try{let o=await Nl(t,n,r);if(o){let s=o.code==="node_not_found"||o.code==="field_not_found"?404:400;return S(e,s,o.message,o.code,ue(o.code)),!0}}catch(o){let s=P(o);return s?(S(e,s.statusCode,s.body.error,s.body.code,ue(s.body.code)),!0):(_(e,o),!0)}return!1}a(Fl,"rejectInvalidFieldTargets");function Ns(e,t){return async(n,r)=>{if(_t(n,r))return;T(r);let o=A(n.query,"flow"),s=A(n.query,"rev"),i=A(n.query,"node"),d=A(n.query,"field"),l=A(n.query,"summary"),c=A(n.query,"title")??l,p=A(n.query,"mode"),u=A(n.query,"targets");if(!o||!s){S(r,400,"flow and rev query parameters are required","missing_draft_params",ue("missing_draft_params"));return}if(!n.accessLink){S(r,401,"Access link required","access_link_missing");return}try{let h=await t.getFlow(o);if(h.rev!==s){r.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:s,currentRev:h.rev,hint:ue("version_mismatch")});return}}catch(h){let y=P(h);if(y){S(r,y.statusCode,y.body.error,y.body.code,y.body.code==="flow_not_found"?X("flow_not_found","get")??"Use an id from GET ./flows":ue(y.body.code));return}_(r,h);return}let f=Il(i,d,p,u);if(!f.ok){S(r,f.status,f.error,f.code,f.hint);return}if(f.mode==="field"){let h=f.targets&&f.targets.length>0?f.targets:i&&d?[{nodeId:i,field:d}]:[];if(h.length>0&&await Fl(r,t,o,h))return}let m=e.create({linkId:n.accessLink.id,flowId:o,expectedRev:s,mode:f.mode,...i?{nodeId:i}:{},...d?{field:d}:{},...f.targets?{targets:f.targets}:{},...l?{summary:l}:{},...c?{title:c}:{}});r.status(201).json({ok:!0,draftId:m.id,mode:m.mode,expiresAt:new Date(m.expiresAtMs).toISOString(),maxBytes:E,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(Ns,"getLinkDraftNewHandler");function Is(e){return(t,n)=>{if(_t(t,n))return;T(n);let r=t.params.draftId,o=A(t.query,"seq"),s=A(t.query,"data");if(typeof r!="string"||!r){S(n,400,"Missing draft id","missing_draft_id");return}if(!t.accessLink){S(n,401,"Access link required","access_link_missing");return}if(o===void 0||s===void 0){S(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 We||d instanceof ze||d instanceof R){S(n,d.statusCode,d.message,d.code,ue(d.code),d instanceof ze?{proposalId:d.proposalId}:void 0);return}_(n,d);return}}}a(Is,"getLinkDraftAppendHandler");function Dl(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 R("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 R("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 R(`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 R("Invalid multi-target draft JSON","invalid_draft_body")}if(e.nodeId&&e.field)return[{nodeId:e.nodeId,set:{[e.field]:t}}];throw new R("Field draft missing node/field","invalid_draft")}a(Dl,"editsFromFieldDraft");function Fs(e,t,n,r,o,s){return async(i,d)=>{if(_t(i,d))return;T(d);let l=i.params.draftId,c=A(i.query,"sha256"),p=A(i.query,"title");if(typeof l!="string"||!l){S(d,400,"Missing draft id","missing_draft_id");return}if(!i.accessLink){S(d,401,"Access link required","access_link_missing");return}if(!c){S(d,400,"sha256 query parameter is required","missing_sha256","Pass sha256 hex of the assembled UTF-8 bytes (before base64url).");return}try{let{draft:u,assembled:f}=e.assembleAndVerify(l,i.accessLink.id,c),m=u.flowId,h,y=u.expectedRev;if(u.mode==="field"){let W=Dl(u,f),oe=await Wt(r,u.flowId,W);if(oe.expectedRev!==u.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:u.expectedRev,currentRev:oe.expectedRev,draftDeleted:!1,hint:"Draft was kept. Re-read the flow and start a new draft with the current rev (or retry if you refresh rev)."});return}h=oe.flow,y=oe.expectedRev}else{let W=Jt(JSON.parse(f),u.flowId);if(m=W.flowId,W.kind==="replace_flow")h={...W.flow,id:W.flow.id||m,type:W.flow.type??"tab",nodes:W.flow.nodes??[]};else{let oe=await qe(r,m,W.ops,{expectedRev:u.expectedRev,...s?{nodeTypes:s}:{}});if(oe.expectedRev!==u.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:u.expectedRev,currentRev:oe.expectedRev,draftDeleted:!1,hint:"Draft was kept. Re-read the flow and start a new draft with the current rev."});return}h=oe.flow,y=oe.expectedRev}}let x=p??u.title??u.summary,k=N(i,o),re=K(i.query),U=await Ne(t,n,i.accessLink,{type:"replace_flow",expectedRev:y,payload:{flowId:m,flow:h},...x?{title:x}:{}},r,k,s);e.markSubmitted(l,i.accessLink.id,U.id);let L={...Z(U,{...k,include:re}),mine:!0},Nt=O(t,U.expectedRev,{flowId:U.payload.flowId}),et=ye(t,t.get(U.id),i.accessLink.id);d.status(201).json({ok:!0,proposal:L,sha256:(0,Cs.createHash)("sha256").update(Buffer.from(f,"utf8")).digest("hex"),...we(et),...H(Nt),hint:`Waiting for human approval. Tell the user about "${L.displayName}" (${L.reviewUrl}). Poll GET \u2026/proposals/<id> for status.`})}catch(u){if(u instanceof ze){S(d,u.statusCode,u.message,u.code,ue(u.code),{proposalId:u.proposalId});return}if(u instanceof We||u instanceof R){S(d,u.statusCode,u.message,u.code,ue(u.code));return}if(u instanceof SyntaxError){S(d,400,"Assembled draft is not valid JSON","invalid_draft_body");return}let f=P(u);if(f){d.status(f.statusCode).json({...f.body,...ue(f.body.code)?{hint:ue(f.body.code)}:{}});return}_(d,u);return}}}a(Fs,"getLinkDraftSubmitHandler");D();be();In();Ce();ce();En();var Ol={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 Yr(e,t){return X(e,"get",{cause:t})??Ol[e]??Xt(e)}a(Yr,"proposeErrorHint");function Ml(e,t,n){e.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:t,currentRev:n,hint:Yr("version_mismatch")})}a(Ml,"sendVersionMismatch");async function $l(e,t,n,r,o){let s=Jt(zt(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 qe(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($l,"buildFromPatch");async function jl(e,t,n,r,o){let s=await Wt(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(jl,"buildFromSetProps");async function Hl(e,t,n,r,o,s,i,d){let l=await qe(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(Hl,"buildFromReplaceText");function Bl(e){let t=Object.prototype.hasOwnProperty.call(e.query,"replace");return{flowId:A(e.query,"flow"),rev:A(e.query,"rev"),nodeId:A(e.query,"node"),patchEncoded:A(e.query,"patch"),setProps:Mr(e.query),findText:A(e.query,"find"),replaceText:t?A(e.query,"replace")??"":void 0,fieldName:A(e.query,"field"),occurrenceRaw:A(e.query,"occurrence")}}a(Bl,"readProposeQuery");function fe(e,t,n){S(e,400,n,t,Yr(t))}a(fe,"jsonProposeError");function ql(e,t){if(t===void 0||t==="")return{ok:!0};let n=Number(t);return Number.isFinite(n)?{ok:!0,occurrence:Math.trunc(n)}:(fe(e,"invalid_occurrence","occurrence must be a positive integer"),{ok:!1})}a(ql,"parseOccurrenceQuery");async function Ul(e,t,n,r,o){if(n.patchEncoded)return $l(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 fe(e,"conflicting_edits","Cannot combine replace_text (find/replace/field) with set.<prop> in one request"),null;if(!n.flowId)return fe(e,"missing_flow_id","flow query parameter is required"),null;if(!n.nodeId)return fe(e,"missing_node_id","node query parameter is required"),null;if(!n.fieldName)return fe(e,"missing_field","field query parameter is required for replace_text"),null;if(n.findText===void 0||n.findText.length===0)return fe(e,"missing_find","find query parameter must be a non-empty string"),null;if(n.replaceText===void 0)return fe(e,"invalid_replace","replace query parameter is required (may be empty)"),null;let i=ql(e,n.occurrenceRaw);return i.ok?Hl(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?(fe(e,"missing_edits","At least one set.<prop>=value is required"),null):jl(t,n.flowId,n.nodeId,n.setProps,r):(fe(e,"missing_node_id","node query parameter is required (or pass patch=)"),null):(fe(e,"missing_flow_id","flow query parameter is required"),null)}a(Ul,"buildProposeFromQuery");function Ds(e,t,n,r,o){return async(s,i)=>{if(_t(s,i))return;T(i);let d=Bl(s),l=A(s.query,"summary"),c=A(s.query,"title")??l;if(!d.rev){fe(i,"missing_expected_rev","rev query parameter is required");return}if(!s.accessLink){S(i,401,"Access link required","access_link_missing");return}try{let p=await Ul(i,n,d,d.rev,o);if(!p)return;if(!p.ok){Ml(i,p.expectedRev,p.currentRev);return}let u=N(s,r),f=K(s.query),m=await Ne(e,t,s.accessLink,{type:"replace_flow",expectedRev:p.expectedRev,payload:{flowId:p.builtFlowId,flow:p.builtFlow},...c?{title:c}:{}},n,u,o),h={...Z(m,{...u,include:f}),mine:!0},y=O(e,m.expectedRev,{flowId:m.payload.flowId}),x=ye(e,e.get(m.id),s.accessLink.id),k=m.warnings;i.status(201).json({ok:!0,proposal:h,...l?{summary:l}:{},...we(x),...H(y),...k&&k.length>0?{warnings:k}:{},hint:`Waiting for human approval. Tell the user about "${h.displayName}" (${h.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(p){let u=P(p,"get");if(u){let f=Yr(u.body.code,u.body.cause);i.status(u.statusCode).json({...u.body,...f?{hint:f}:{}});return}_(i,p)}}}a(Ds,"getLinkProposeHandler");D();be();D();var Gl=50,Os=40,Wl=20;function zl(e){return Buffer.byteLength(JSON.stringify(e),"utf8")}a(zl,"nodeBytes");function Jl(e){let t={id:e.id,type:e.type,bytes:zl(e)};return typeof e.name=="string"&&(t.name=e.name),"wires"in e&&(t.wires=e.wires),t}a(Jl,"summarizeNode");async function Hn(e,t){let{rev:n,flow:r}=await pe(e,t),o=r,s=Array.isArray(o.nodes)?o.nodes.map(Jl):[],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(Hn,"getFlowSummary");function Ms(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(Ms,"collectTabs");async function $s(e,t,n){if(!t)throw new g("Missing node id","missing_node_id");if(n){let{rev:d,flow:l}=await pe(e,n),c=l,p=Array.isArray(c.nodes)?c.nodes:[],u=Array.isArray(c.configs)?c.configs:[],f=[...p,...u].find(m=>m.id===t);if(!f)throw new g("Node was not found","node_not_found",{statusCode:404});return{rev:d,flowId:n,node:f}}let{rev:r,flows:o}=await te(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 Ms(o)){let l=d.id;if(l)try{let p=(await pe(e,l)).flow,f=[...p.nodes??[],...p.configs??[]].find(m=>m.id===t);if(f){s=f,i=l;break}}catch{}}if(!s)throw new g("Node was not found","node_not_found",{statusCode:404});return{rev:r,flowId:i,node:s}}a($s,"findNodeInFlows");async function Bn(e,t,n){let r=await $s(e,t,n);return{rev:r.rev,flowId:r.flowId,node:r.node}}a(Bn,"getNodeById");function Kl(e){if(e===void 0||e==="")return;let t=/^(\d+)(?:-(\d+))?$/.exec(e.trim());if(!t)throw new g("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 g("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");if(r<n)throw new g("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");return{start:n,end:r}}a(Kl,"parseLineRange");async function qn(e,t,n,r={}){if(!n)throw new g("Missing field name","missing_field");let o=await $s(e,t,r.flowId);if(!(n in o.node))throw new g("Field was not found on the node","field_not_found");let s=o.node[n];if(typeof s!="string")throw new g("Field is not a string","field_not_string");let i=s.split(`
|
|
914
|
-
`),d=i.length,l=Buffer.byteLength(s,"utf8"),c=
|
|
915
|
-
`);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(
|
|
923
|
+
`}a(tc,"docsErrorsAndAlso");function Fs(e){let{link:t,openapiPath:n,restApiBase:r,mcpRpcPath:o,publicOrigin:s}=e,i=Pt(t.expiresAt),d=t.label?` (${Pt(t.label)})`:"",l=Pt(n),c=Pt(r),p=Pt(o);return[Jl(d,i),zl(c,l,p,s?Pt(`${s}${o}`):Is),Kl(),Vl(),Xl(),Zl(),Yl(),Ql(),ec(),tc()].join(`
|
|
924
|
+
`)}a(Fs,"renderGetLinkDocs");var Gs=require("crypto");$();O();be();Ne();ce();O();function Fn(e){return{error:e instanceof Error&&e.message.trim()?e.message:"Internal server error",code:"internal_error"}}a(Fn,"internalErrorBody");function nc(e,t){e.headersSent||e.status(500).json(Fn(t))}a(nc,"sendInternalError");function P(e,t){if(e.headersSent)return;let n=_(t);if(n){e.status(n.statusCode).json(n.body);return}nc(e,t)}a(P,"sendCaughtRouteError");function v(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}P(n,o)})}}a(v,"asyncRoute");var Dn=require("crypto");$();xe();var R=class extends Error{static{a(this,"DraftValidationError")}statusCode=400;code;constructor(t,n="draft_invalid"){super(t),this.name="DraftValidationError",this.code=n}},We=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"}},ze=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}},rc=/^[A-Za-z0-9_-]+={0,2}$/;function Os(e){if(!rc.test(e)||e.replace(/=+$/,"").length===0)throw new R("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 R("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data")}if(t.byteLength===0&&e.length>0)throw new R("data must be non-empty base64url (A-Z, a-z, 0-9, -, _)","invalid_chunk_data");return t}a(Os,"assertValidBase64UrlChunk");var On=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,Dn.randomBytes)(12).toString("hex")),this.maxBytes=t.maxBytes??E,this.ttlMs=t.ttlMs??Eo}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 ze(t,o.proposalId);let s=this.drafts.get(t);if(s?.linkId!==n)throw new We(t);if(s.expiresAtMs<=r)throw new We(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 R("seq must be a non-negative integer","invalid_seq");if(typeof o!="string")throw new R("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 R(`Chunk seq ${r} already stored with different data`,"chunk_conflict")}else{let p=Os(o),u=s.totalBytes+p.byteLength;if(u>this.maxBytes)throw new R(`Draft exceeds limit of ${this.maxBytes} bytes (decoded)`,"payload_too_large");s.chunks.set(r,o),s.totalBytes=u}let d=[...s.chunks.keys()].sort((p,u)=>p-u),l=[],c=d.at(-1);if(c!==void 0)for(let p=0;p<=c;p+=1)s.chunks.has(p)||l.push(p);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 R("Draft has no chunks","draft_empty");let i=[...o.chunks.keys()].sort((m,h)=>m-h).at(-1);if(i===void 0)throw new R("Draft has no chunks","draft_empty");let d=[];for(let m=0;m<=i;m+=1)o.chunks.has(m)||d.push(m);if(d.length>0)throw new R(`Missing chunk seqs: ${d.join(",")}`,"missing_chunks");let l=[];for(let m=0;m<=i;m+=1){let h=o.chunks.get(m);if(h===void 0)throw new R(`Missing chunk seqs: ${m}`,"missing_chunks");l.push(h)}let c=l.map(m=>Os(m)),p=Buffer.concat(c);if(p.byteLength>this.maxBytes)throw new R(`Assembled draft exceeds limit of ${this.maxBytes} bytes`,"payload_too_large");let u=r.trim().toLowerCase();if(!/^[0-9a-f]{64}$/.test(u))throw new R("invalid_sha256: expected 64 hex chars","invalid_sha256");if((0,Dn.createHash)("sha256").update(p).digest("hex")!==u)throw new R("sha256 does not match assembled draft content","draft_hash_mismatch");return{draft:o,assembled:p.toString("utf8")}}size(){return this.purgeExpired(this.now()),this.drafts.size}clear(){this.drafts.clear(),this.submitted.clear()}};Rn();$e();xe();var Mn=ca(require("express-rate-limit"),1);var Xr=6e4,Ds=600,Ms=300;function Vr(e,t){return typeof e=="number"&&Number.isFinite(e)&&e>0?Math.floor(e):t}a(Vr,"positiveInt");function Yr(e){if(!e)return{windowMs:Xr,maxPerIp:Ds,maxPerToken:Ms};let t=e;return{windowMs:Vr(t.windowMs??t.rateLimitWindowMs,Xr),maxPerIp:Vr(t.maxPerIp??t.rateLimitMaxPerIp,Ds),maxPerToken:Vr(t.maxPerToken??t.rateLimitMaxPerToken,Ms)}}a(Yr,"resolveRateLimitConfig");function oc(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(oc,"retryAfterSeconds");function sc(e,t,n=Xr){let r=oc(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(sc,"sendRateLimited");function ic(e){return(t,n,r,o)=>{sc(t,n,e)}}a(ic,"rateLimitedHandler");function js(e){return`ip:${(0,Mn.ipKeyGenerator)(e.ip??"unknown")}`}a(js,"clientIpKey");function $s(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($s,"accessTokenRateLimitKey");function Hs(e,t,n,r){let o={windowMs:t,max:e,standardHeaders:!0,legacyHeaders:!1,keyGenerator:n,handler:ic(t)};return r&&(o.skip=r),(0,Mn.default)(o)}a(Hs,"createLimiter");function ac(e){let t=Yr(e);return Hs(t.maxPerIp,t.windowMs,js)}a(ac,"createAiAccessRateLimiter");function dc(e){let t=Yr(e);return Hs(t.maxPerToken,t.windowMs,n=>$s(n)??js(n),n=>$s(n)==null)}a(dc,"createAiAccessTokenRateLimiter");function $n(e){let t=Yr(e),n=ac(t),r=dc(t);return{ip:n,token:r,stack:[n,r],config:t}}a($n,"createAiAccessDualRateLimiters");Ae();function T(e){e.setHeader("Cache-Control","no-store"),e.setHeader("Pragma","no-cache")}a(T,"setNoStore");function Qr(e){let t=e.originalUrl||e.url||"",n=wr(t);n!==t&&(Object.defineProperty(e,"originalUrl",{value:n,writable:!0,configurable:!0}),typeof e.url=="string"&&Object.defineProperty(e,"url",{value:wr(e.url),writable:!0,configurable:!0}))}a(Qr,"redactTokenFromRequestUrl");function Bs(e,t){return(n,r,o)=>{T(r);let s=n.originalUrl||n.url||"";if(n.getLinkUrlBytes=Buffer.byteLength(s,"utf8"),Qr(n),!ie(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",j),r.status(401).json({error:Q,code:ge,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"){jt(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",j),r.status(401).json({error:Me,code:$t,hint:c});return}if(l.link.kind!=="get_link"){Ht(r,"get_link",l.link.kind);return}n.accessLink=l.link,n.getLinkToken=d,o()}}a(Bs,"requireGetLinkToken");function Rt(e,t){let n=e.getLinkUrlBytes;return(typeof n=="number"?n:Buffer.byteLength(e.originalUrl||e.url||"","utf8"))>ot?(T(t),t.status(414).json({error:`Request URL exceeds ${ot} 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:ot}),!0):!1}a(Rt,"rejectOversizedGetUrl");function S(e,t,n,r,o,s){T(e),e.status(t).json({error:n,code:r,...o?{hint:o}:{},...s})}a(S,"jsonError");function qs(){return(e,t)=>{Qr(e),T(t),t.status(404).json({error:"Not found",code:"not_found"})}}a(qs,"getLinkNotFoundHandler");var lc="link_create_not_supported";function Us(){return(e,t)=>{Qr(e),T(t),t.status(403).json({error:"GET access links cannot create tokens",code:lc,hint:"Ask an admin with * permission to mint tokens via POST /mcp/api/v1/links on the /mcp Access links page. This get_link URL cannot create, list, or revoke tokens."})}}a(Us,"getLinkMintNotSupportedHandler");function A(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(A,"queryString");function ue(e){switch(e){case"draft_not_found":return"Start a new draft with GET \u2026/draft/new.";case"draft_already_submitted":return"This draft already created a proposal; poll that proposal or start a new draft.";case"chunk_conflict":return"Re-send the same data for that seq, or start a new draft.";case"payload_too_large":return"Shrink the content; totalBytes counts decoded bytes against maxBytes.";case"invalid_seq":return"seq must be a non-negative integer (0, 1, 2, \u2026).";case"invalid_chunk":case"invalid_chunk_data":return"data must be base64url of the raw chunk bytes (A-Z, a-z, 0-9, -, _).";case"missing_chunks":return"Upload every seq from 0 through max before submit.";case"invalid_sha256":return"sha256 must be exactly 64 hexadecimal characters.";case"draft_hash_mismatch":return"sha256 must be the hex digest of the assembled UTF-8 bytes (before base64url).";case"draft_empty":return"Append at least one chunk before submit.";case"node_not_found":return"Call GET \u2026/flows/<id> and use a node id from that response (or use mode=json with op:add).";case"field_not_found":return"Use a property that exists on the node, or mode=json to add/replace nodes.";case"missing_draft_params":return"Example: \u2026/draft/new?flow=tab1&rev=abc&node=n1&field=template";case"version_mismatch":return"Re-read the flow and start a new draft with the current rev.";case"protected_property":return"Omit id, type, and z from set props. wires are allowed when targets exist in the flow.";case"invalid_wires":return"wires must list existing node ids (or add those nodes in the same patch).";default:return}}a(ue,"draftErrorHint");async function cc(e,t,n){let r;try{r=await e.getFlow(t)}catch(s){let i=_(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(cc,"validateFieldDraftTargets");function pc(e,t,n,r){if(r)try{let o=Jt(r);if(!Array.isArray(o)||o.length===0)throw new Error("targets must be a non-empty array");return{ok:!0,mode:"field",targets:o.map(i=>{if(!i||typeof i!="object")throw new Error("bad target");let d=i,l=typeof d.nodeId=="string"?d.nodeId:typeof d.node=="string"?d.node:"",c=typeof d.field=="string"?d.field:"";if(!l||!c)throw new Error("bad target");return{nodeId:l,field:c}})}}catch{return{ok:!1,status:400,error:"targets must be base64url JSON array of {nodeId,field}",code:"invalid_targets"}}if(n==="json"||!e&&!t)return{ok:!0,mode:"json"};if(!e||!t){let o=ue("missing_draft_params");return{ok:!1,status:400,error:"node and field are required for field drafts (or pass mode=json)",code:"missing_draft_params",...o?{hint:o}:{}}}return{ok:!0,mode:"field"}}a(pc,"planDraftMode");async function uc(e,t,n,r){try{let o=await cc(t,n,r);if(o){let s=o.code==="node_not_found"||o.code==="field_not_found"?404:400;return S(e,s,o.message,o.code,ue(o.code)),!0}}catch(o){let s=_(o);return s?(S(e,s.statusCode,s.body.error,s.body.code,ue(s.body.code)),!0):(P(e,o),!0)}return!1}a(uc,"rejectInvalidFieldTargets");function Ws(e,t){return async(n,r)=>{if(Rt(n,r))return;T(r);let o=A(n.query,"flow"),s=A(n.query,"rev"),i=A(n.query,"node"),d=A(n.query,"field"),l=A(n.query,"summary"),c=A(n.query,"title")??l,p=A(n.query,"mode"),u=A(n.query,"targets");if(!o||!s){S(r,400,"flow and rev query parameters are required","missing_draft_params",ue("missing_draft_params"));return}if(!n.accessLink){S(r,401,"Access link required","access_link_missing");return}try{let h=await t.getFlow(o);if(h.rev!==s){r.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:s,currentRev:h.rev,hint:ue("version_mismatch")});return}}catch(h){let y=_(h);if(y){S(r,y.statusCode,y.body.error,y.body.code,y.body.code==="flow_not_found"?X("flow_not_found","get")??"Use an id from GET ./flows":ue(y.body.code));return}P(r,h);return}let f=pc(i,d,p,u);if(!f.ok){S(r,f.status,f.error,f.code,f.hint);return}if(f.mode==="field"){let h=f.targets&&f.targets.length>0?f.targets:i&&d?[{nodeId:i,field:d}]:[];if(h.length>0&&await uc(r,t,o,h))return}let m=e.create({linkId:n.accessLink.id,flowId:o,expectedRev:s,mode:f.mode,...i?{nodeId:i}:{},...d?{field:d}:{},...f.targets?{targets:f.targets}:{},...l?{summary:l}:{},...c?{title:c}:{}});r.status(201).json({ok:!0,draftId:m.id,mode:m.mode,expiresAt:new Date(m.expiresAtMs).toISOString(),maxBytes:E,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(Ws,"getLinkDraftNewHandler");function zs(e){return(t,n)=>{if(Rt(t,n))return;T(n);let r=t.params.draftId,o=A(t.query,"seq"),s=A(t.query,"data");if(typeof r!="string"||!r){S(n,400,"Missing draft id","missing_draft_id");return}if(!t.accessLink){S(n,401,"Access link required","access_link_missing");return}if(o===void 0||s===void 0){S(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 We||d instanceof ze||d instanceof R){S(n,d.statusCode,d.message,d.code,ue(d.code),d instanceof ze?{proposalId:d.proposalId}:void 0);return}P(n,d);return}}}a(zs,"getLinkDraftAppendHandler");function fc(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 R("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 R("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 R(`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 R("Invalid multi-target draft JSON","invalid_draft_body")}if(e.nodeId&&e.field)return[{nodeId:e.nodeId,set:{[e.field]:t}}];throw new R("Field draft missing node/field","invalid_draft")}a(fc,"editsFromFieldDraft");function Js(e,t,n,r,o,s){return async(i,d)=>{if(Rt(i,d))return;T(d);let l=i.params.draftId,c=A(i.query,"sha256"),p=A(i.query,"title");if(typeof l!="string"||!l){S(d,400,"Missing draft id","missing_draft_id");return}if(!i.accessLink){S(d,401,"Access link required","access_link_missing");return}if(!c){S(d,400,"sha256 query parameter is required","missing_sha256","Pass sha256 hex of the assembled UTF-8 bytes (before base64url).");return}try{let{draft:u,assembled:f}=e.assembleAndVerify(l,i.accessLink.id,c),m=u.flowId,h,y=u.expectedRev;if(u.mode==="field"){let W=fc(u,f),oe=await zt(r,u.flowId,W);if(oe.expectedRev!==u.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:u.expectedRev,currentRev:oe.expectedRev,draftDeleted:!1,hint:"Draft was kept. Re-read the flow and start a new draft with the current rev (or retry if you refresh rev)."});return}h=oe.flow,y=oe.expectedRev}else{let W=Kt(JSON.parse(f),u.flowId);if(m=W.flowId,W.kind==="replace_flow")h={...W.flow,id:W.flow.id||m,type:W.flow.type??"tab",nodes:W.flow.nodes??[]};else{let oe=await qe(r,m,W.ops,{expectedRev:u.expectedRev,...s?{nodeTypes:s}:{}});if(oe.expectedRev!==u.expectedRev){d.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:u.expectedRev,currentRev:oe.expectedRev,draftDeleted:!1,hint:"Draft was kept. Re-read the flow and start a new draft with the current rev."});return}h=oe.flow,y=oe.expectedRev}}let x=p??u.title??u.summary,k=N(i,o),re=K(i.query),U=await Ie(t,n,i.accessLink,{type:"replace_flow",expectedRev:y,payload:{flowId:m,flow:h},...x?{title:x}:{}},r,k,s);e.markSubmitted(l,i.accessLink.id,U.id);let L={...Z(U,{...k,include:re}),mine:!0},It=D(t,U.expectedRev,{flowId:U.payload.flowId}),et=ye(t,t.get(U.id),i.accessLink.id);d.status(201).json({ok:!0,proposal:L,sha256:(0,Gs.createHash)("sha256").update(Buffer.from(f,"utf8")).digest("hex"),...we(et),...H(It),hint:`Waiting for human approval. Tell the user about "${L.displayName}" (${L.reviewUrl}). Poll GET \u2026/proposals/<id> for status.`})}catch(u){if(u instanceof ze){S(d,u.statusCode,u.message,u.code,ue(u.code),{proposalId:u.proposalId});return}if(u instanceof We||u instanceof R){S(d,u.statusCode,u.message,u.code,ue(u.code));return}if(u instanceof SyntaxError){S(d,400,"Assembled draft is not valid JSON","invalid_draft_body");return}let f=_(u);if(f){d.status(f.statusCode).json({...f.body,...ue(f.body.code)?{hint:ue(f.body.code)}:{}});return}P(d,u);return}}}a(Js,"getLinkDraftSubmitHandler");O();be();Nn();Ne();ce();Rn();var mc={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 Zr(e,t){return X(e,"get",{cause:t})??mc[e]??Yt(e)}a(Zr,"proposeErrorHint");function gc(e,t,n){e.status(409).json({error:"Flow revision mismatch",code:"version_mismatch",expectedRev:t,currentRev:n,hint:Zr("version_mismatch")})}a(gc,"sendVersionMismatch");async function hc(e,t,n,r,o){let s=Kt(Jt(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 qe(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(hc,"buildFromPatch");async function yc(e,t,n,r,o){let s=await zt(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(yc,"buildFromSetProps");async function wc(e,t,n,r,o,s,i,d){let l=await qe(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(wc,"buildFromReplaceText");function bc(e){let t=Object.prototype.hasOwnProperty.call(e.query,"replace");return{flowId:A(e.query,"flow"),rev:A(e.query,"rev"),nodeId:A(e.query,"node"),patchEncoded:A(e.query,"patch"),setProps:jr(e.query),findText:A(e.query,"find"),replaceText:t?A(e.query,"replace")??"":void 0,fieldName:A(e.query,"field"),occurrenceRaw:A(e.query,"occurrence")}}a(bc,"readProposeQuery");function fe(e,t,n){S(e,400,n,t,Zr(t))}a(fe,"jsonProposeError");function vc(e,t){if(t===void 0||t==="")return{ok:!0};let n=Number(t);return Number.isFinite(n)?{ok:!0,occurrence:Math.trunc(n)}:(fe(e,"invalid_occurrence","occurrence must be a positive integer"),{ok:!1})}a(vc,"parseOccurrenceQuery");async function kc(e,t,n,r,o){if(n.patchEncoded)return hc(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 fe(e,"conflicting_edits","Cannot combine replace_text (find/replace/field) with set.<prop> in one request"),null;if(!n.flowId)return fe(e,"missing_flow_id","flow query parameter is required"),null;if(!n.nodeId)return fe(e,"missing_node_id","node query parameter is required"),null;if(!n.fieldName)return fe(e,"missing_field","field query parameter is required for replace_text"),null;if(n.findText===void 0||n.findText.length===0)return fe(e,"missing_find","find query parameter must be a non-empty string"),null;if(n.replaceText===void 0)return fe(e,"invalid_replace","replace query parameter is required (may be empty)"),null;let i=vc(e,n.occurrenceRaw);return i.ok?wc(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?(fe(e,"missing_edits","At least one set.<prop>=value is required"),null):yc(t,n.flowId,n.nodeId,n.setProps,r):(fe(e,"missing_node_id","node query parameter is required (or pass patch=)"),null):(fe(e,"missing_flow_id","flow query parameter is required"),null)}a(kc,"buildProposeFromQuery");function Ks(e,t,n,r,o){return async(s,i)=>{if(Rt(s,i))return;T(i);let d=bc(s),l=A(s.query,"summary"),c=A(s.query,"title")??l;if(!d.rev){fe(i,"missing_expected_rev","rev query parameter is required");return}if(!s.accessLink){S(i,401,"Access link required","access_link_missing");return}try{let p=await kc(i,n,d,d.rev,o);if(!p)return;if(!p.ok){gc(i,p.expectedRev,p.currentRev);return}let u=N(s,r),f=K(s.query),m=await Ie(e,t,s.accessLink,{type:"replace_flow",expectedRev:p.expectedRev,payload:{flowId:p.builtFlowId,flow:p.builtFlow},...c?{title:c}:{}},n,u,o),h={...Z(m,{...u,include:f}),mine:!0},y=D(e,m.expectedRev,{flowId:m.payload.flowId}),x=ye(e,e.get(m.id),s.accessLink.id),k=m.warnings;i.status(201).json({ok:!0,proposal:h,...l?{summary:l}:{},...we(x),...H(y),...k&&k.length>0?{warnings:k}:{},hint:`Waiting for human approval. Tell the user about "${h.displayName}" (${h.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(p){let u=_(p,"get");if(u){let f=Zr(u.body.code,u.body.cause);i.status(u.statusCode).json({...u.body,...f?{hint:f}:{}});return}P(i,p)}}}a(Ks,"getLinkProposeHandler");O();be();O();var Ac=50,Vs=40,Sc=20;function xc(e){return Buffer.byteLength(JSON.stringify(e),"utf8")}a(xc,"nodeBytes");function Tc(e){let t={id:e.id,type:e.type,bytes:xc(e)};return typeof e.name=="string"&&(t.name=e.name),"wires"in e&&(t.wires=e.wires),t}a(Tc,"summarizeNode");async function jn(e,t){let{rev:n,flow:r}=await pe(e,t),o=r,s=Array.isArray(o.nodes)?o.nodes.map(Tc):[],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(jn,"getFlowSummary");function Xs(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(Xs,"collectTabs");async function Ys(e,t,n){if(!t)throw new g("Missing node id","missing_node_id");if(n){let{rev:d,flow:l}=await pe(e,n),c=l,p=Array.isArray(c.nodes)?c.nodes:[],u=Array.isArray(c.configs)?c.configs:[],f=[...p,...u].find(m=>m.id===t);if(!f)throw new g("Node was not found","node_not_found",{statusCode:404});return{rev:d,flowId:n,node:f}}let{rev:r,flows:o}=await te(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 Xs(o)){let l=d.id;if(l)try{let p=(await pe(e,l)).flow,f=[...p.nodes??[],...p.configs??[]].find(m=>m.id===t);if(f){s=f,i=l;break}}catch{}}if(!s)throw new g("Node was not found","node_not_found",{statusCode:404});return{rev:r,flowId:i,node:s}}a(Ys,"findNodeInFlows");async function Hn(e,t,n){let r=await Ys(e,t,n);return{rev:r.rev,flowId:r.flowId,node:r.node}}a(Hn,"getNodeById");function _c(e){if(e===void 0||e==="")return;let t=/^(\d+)(?:-(\d+))?$/.exec(e.trim());if(!t)throw new g("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 g("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");if(r<n)throw new g("lines must be A or A-B (1-based inclusive; end >= start)","invalid_line_range");return{start:n,end:r}}a(_c,"parseLineRange");async function Bn(e,t,n,r={}){if(!n)throw new g("Missing field name","missing_field");let o=await Ys(e,t,r.flowId);if(!(n in o.node))throw new g("Field was not found on the node","field_not_found");let s=o.node[n];if(typeof s!="string")throw new g("Field is not a string","field_not_string");let i=s.split(`
|
|
925
|
+
`),d=i.length,l=Buffer.byteLength(s,"utf8"),c=_c(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 g(`lines range is out of bounds (start ${c.start} > ${d} total lines)`,"line_range_out_of_bounds",{details:{totalLines:d}});let p=c.start-1,u=Math.min(c.end,i.length),f=i.slice(p,u).join(`
|
|
926
|
+
`);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(Bn,"getNodeField");function Pc(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(Pc,"stringFields");function Rc(e,t,n){let r=Math.max(0,t-Vs),o=Math.min(e.length,t+n+Vs),s=e.slice(r,o).replace(/\s+/g," ");return r>0&&(s=`\u2026${s}`),o<e.length&&(s=`${s}\u2026`),s}a(Rc,"snippetAround");function Ec(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(Ec,"lineNumberAtIndex");function eo(e,t,n,r,o){for(let{field:s,value:i}of Pc(e)){if(r.length>=o)return!0;let d=[],l=new Set,c=-1,p=a((f,m)=>{l.has(f)||(l.add(f),d.push(f)),c<0&&(c=m)},"pushLine");if(!n.includes(`
|
|
916
927
|
`)){let f=i.split(`
|
|
917
928
|
`);for(let m=0;m<f.length;m+=1){let y=(f[m]??"").indexOf(n);if(y>=0){let x=0;for(let k=0;k<m;k+=1)x+=(f[k]?.length??0)+1;p(m+1,x+y)}}}if(n.includes(`
|
|
918
|
-
`)){let f=0;for(;f<=i.length;){let m=i.indexOf(n,f);if(m<0)break;p(
|
|
919
|
-
`)}a(qs,"splitLines");ce();var zs=new Set(["func","template","info","payload","code","text","format","syntax"]),nc=new Set(["x","y","w","h","wires","g"]),rc=new Set(["w","h","g"]);function Ke(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(Ke,"nodeName");function Gs(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(Gs,"collectNodes");function tr(e){if(e===void 0)return"undefined";if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(tr).join(",")}]`;let t=e;return`{${Object.keys(t).sort().map(r=>`${JSON.stringify(r)}:${tr(t[r])}`).join(",")}}`}a(tr,"stableStringify");function Js(e,t){return tr(e)===tr(t)}a(Js,"valuesEqual");function nr(e){return typeof e=="string"?e:null}a(nr,"asText");function oc(e){return/<\/?[a-z][\s\S]*>/i.test(e.slice(0,800))}a(oc,"looksLikeHtml");function sc(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:oc(n)}a(sc,"isHtmlTemplateField");function Ks(e,t,n){let r=nr(n);if(zs.has(t)&&r!==null){let o={key:t,kind:"text",value:r};return sc(e,t,r)&&(o.preview="html"),o}return{key:t,kind:"scalar",value:n===void 0?null:n}}a(Ks,"toPresenceProperty");function ic(e,t){let n=[];for(let r of[...Object.keys(e)].sort())if(!(rc.has(r)||r==="id"||r==="z")){if(r==="wires"){let o=Vs(e,t);n.push({key:"wires",kind:"scalar",value:o.length>0?o:e.wires??null});continue}n.push(Ks(e,r,e[r]))}return n}a(ic,"nodeProperties");function Vs(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?Ke(l)??l.type:d})}r.push({port:s,to:i})}),r}a(Vs,"describeWiresStructured");var ac=new Set(["func","template","code","text"]);function dc(e,t){let n=[],r=Ke(e);r&&n.push({key:"name",kind:"scalar",value:r});let o=Vs(e,t);o.length>0&&n.push({key:"wires",kind:"scalar",value:o});for(let s of ac){if(!(s in e))continue;let i=nr(e[s]);i===null||i.length===0||n.push(Ks(e,s,i))}return n}a(dc,"buildKeyFields");function lc(e,t,n){let r=Le(e.type),o=Ke(e),s=o?` "${o}"`:"",i=t?` in "${t}"`:"",d=n===1?"1 field":`${n} fields`;return`Changes ${r} node${s}${i} (${d})`}a(lc,"buildModifiedSummary");function cc(e,t,n){let r=Le(e.type),o=Ke(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 p=typeof e.field=="string"&&e.field?e.field:"";p&&(c+=`, output to msg.${p}`)}return c}a(cc,"buildPresenceSummary");function Zn(e,t,n,r){let o={id:e.id,type:e.type,summary:cc(e,t,n),keyFields:dc(e,r),properties:ic(e,r)},s=Ke(e);return s!==void 0&&(o.name=s),o}a(Zn,"toPresenceNode");function pc(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let o of[...n].sort()){if(nc.has(o)||o==="id"||o==="z")continue;let s=e[o],i=t[o];if(Js(s,i))continue;let d=nr(s),l=nr(i);if(zs.has(o)&&(d!==null||l!==null)){let c=d??"",p=l??"";r.push({key:o,kind:"text",before:c,after:p,lines:Us(c,p)});continue}r.push({key:o,kind:"scalar",before:s===void 0?null:s,after:i===void 0?null:i})}return r}a(pc,"compareNodes");function uc(e,t){let n=["label","disabled","info","env"],r=[];for(let o of n)Js(e[o],t[o])||r.push(o);return r}a(uc,"compareTabMeta");function er(e){return typeof e.label=="string"&&e.label.trim()?e.label.trim():""}a(er,"flowLabelOf");function Xs(e,t){let n=Gs(t),r=Gs(e),o=er(e)||er(t),s=er(t)||er(e),i=[],d=[],l=[];for(let c of e.nodes??[])n.has(c.id)||i.push(Zn(c,"added",o,r));for(let c of e.configs??[])n.has(c.id)||i.push(Zn(c,"added",o,r));for(let c of t.nodes??[])r.has(c.id)||d.push(Zn(c,"removed",s,n));for(let c of t.configs??[])r.has(c.id)||d.push(Zn(c,"removed",s,n));for(let[c,p]of r){let u=n.get(c);if(!u)continue;let f=pc(u,p);if(f.length>0){let m={id:c,type:p.type||u.type,summary:lc(p,o||s,f.length),changes:f},h=Ke(p)??Ke(u);h!==void 0&&(m.name=h),l.push(m)}}return l.sort((c,p)=>c.id.localeCompare(p.id)),{summary:{added:i.length,removed:d.length,modified:l.length,tabChanges:uc(t,e),beforeNodeCount:n.size,afterNodeCount:r.size},added:i,removed:d,modified:l}}a(Xs,"buildDiffSections");function Ws(e,t,n={}){let r=Xs(e.payload.flow,t.flow),o=n.currentRev??t.rev,s=e.expectedRev,i=n.baseRevStale??(e.status==="pending"&&s!==o),d={proposalId:e.id,flowId:e.payload.flowId,expectedRev:s,currentRev:o,baseRevStale:i,compareAgainst:n.compareAgainst??"current",...r};return e.warnings&&e.warnings.length>0&&(d.warnings=e.warnings.map(l=>({...l}))),n.baseSnapshotMissing&&(d.baseSnapshotMissing=!0),n.vsCurrent&&(d.vsCurrent=n.vsCurrent),d}a(Ws,"buildProposalDiff");async function Et(e,t){let n=e.payload.flowId,r={id:n,type:"tab",nodes:[],configs:[]},o=e.expectedRev,s=r;try{let c=await t.getFlow(n);o=c.rev,s=c.flow}catch(c){if(!(c instanceof I))throw c;try{o=(await t.getFlows()).rev}catch{}}let d=e.status==="pending"&&e.expectedRev!==o,l=e.baseFlow;if(l!==void 0){let c=d?Xs(e.payload.flow,s):void 0;return Ws(e,{rev:e.expectedRev,flow:l},{currentRev:o,compareAgainst:"base",baseRevStale:d,...c?{vsCurrent:c}:{}})}return Ws(e,{rev:o,flow:s},{compareAgainst:"current",baseSnapshotMissing:!0,baseRevStale:d})}a(Et,"computeProposalDiff");ce();Se();Ae();var Ys=require("path");function en(e){return(0,Ys.join)(e.userDir,".ai-access")}a(en,"getStorageDir");var fc=["status:read","flows:read","proposals:create","proposals:read-own"];function Qs(){return St()}a(Qs,"readPackageVersion");function mc(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/1e3)):0}a(mc,"secondsRemainingUntil");function gc(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/6e4)):0}a(gc,"minutesRemainingUntil");function hc(e,t){let n=me(t),r=Pe(t),o=`${n}/openapi.yaml`;return e==="rest"?{api:n,openapi:o,discovery:n}:e==="mcp"?{rpc:r,discovery:n}:{discovery:n}}a(hc,"baseUrlsForTokenKind");function Lt(e,t){let n=bo(e,t),r=se(e.settings);if(t.accessLink){let o=Date.now();return{ok:!0,version:Qs(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:null,capabilities:{view:!0,manageLinks:!1,approveProposals:!1},storageDir:en(e.settings),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:gc(t.accessLink.expiresAt,o),secondsRemaining:mc(t.accessLink.expiresAt,o),permissions:[...fc],baseUrls:hc(t.accessLink.kind,e.settings.httpAdminRoot)}}}return{ok:!0,version:Qs(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:n.permission,capabilities:n.capabilities,storageDir:en(e.settings),defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,maxActiveLinks:r.maxActiveLinks,defaultDeploymentType:r.defaultDeploymentType,caller:{kind:"admin",...n.permission?{permission:n.permission}:{}}}}a(Lt,"buildStatus");function Zs(e,t){let n=t.draftStore??new On,r=t.rateLimiters??jn(t.rateLimit??e.settings.aiAccess),o=e.httpAdmin,s="/mcp/a/:token",i=[r.ip,Rs(e,t.linkStore),r.token];o.get(s,...i,v((p,u)=>{T(u),u.setHeader("X-Content-Type-Options","nosniff");let f=me(e.settings.httpAdminRoot),m=p.accessLink;if(!m){S(u,401,"Access link required","access_link_missing");return}let h=ks({link:m,openapiPath:`${f}/openapi.yaml`,restApiBase:f,mcpRpcPath:Pe(e.settings.httpAdminRoot)});u.status(200).type("text/markdown; charset=utf-8").send(h)})),o.get(`${s}/status`,...i,v((p,u)=>{T(u),u.json(Lt(e,p))})),o.get(`${s}/flows`,...i,v(async(p,u)=>{T(u);try{u.json(await te(t.flowAdapter))}catch(f){let m=P(f);if(m){u.status(m.statusCode).json({...m.body,hint:"Retry after the owner fixes the plugin runtime."});return}S(u,502,f instanceof Error?f.message:String(f),"upstream_error")}})),o.get(`${s}/flows/:id`,...i,v(async(p,u)=>{T(u),await zn(t.flowAdapter,"get")(p,u)})),o.get(`${s}/nodes/:id`,...i,v(async(p,u)=>{T(u),await Jn(t.flowAdapter,"get")(p,u)})),o.get(`${s}/nodes/:id/field/:name`,...i,v(async(p,u)=>{T(u),await Kn(t.flowAdapter,"get")(p,u)})),o.get(`${s}/search`,...i,v(async(p,u)=>{T(u),await Vn(t.flowAdapter,"get")(p,u)}));let d={nodeTypes:t.nodeTypesAdapter,...t.catalogueFetcher?{catalogueFetcher:t.catalogueFetcher}:{},...t.catalogueUrl?{catalogueUrl:t.catalogueUrl}:{}};o.get(`${s}/node-types`,...i,v(async(p,u)=>{T(u),await Xn(d)(p,u)})),o.get(`${s}/node-types/:type`,...i,v(async(p,u)=>{T(u),await Yn(d)(p,u)})),o.get(`${s}/palette/search`,...i,v(async(p,u)=>{T(u),await Qn(d)(p,u)}));let l=e.settings.httpAdminRoot;yc(o,s,i,t,l),o.get(`${s}/propose`,...i,v(Ds(t.proposalStore,t.auditLog,t.flowAdapter,l,t.nodeTypesAdapter))),vc(o,s,i,n,t,l);let c=Ls();return o.all(`${s}/links`,c),o.all(`${s}/links/:id`,c),o.use("/mcp/a",Es()),n}a(Zs,"registerGetLinkRoutes");function yc(e,t,n,r,o){e.get(`${t}/proposals`,...n,v(async(s,i)=>{T(i);let d=s.accessLink;if(!d){S(i,401,"Access link required","access_link_missing");return}let l=N(s,o),c=K(s.query),p=Cn(s.query),u;try{u=(await te(r.flowAdapter)).rev}catch{}let f=u?O(r.proposalStore,u):0,m=ht(r.proposalStore,d,l,p);i.json({proposals:m.proposals.map(h=>ee(r.proposalStore,d,h,{...l,include:c},{includeConflicts:!1})),...Ue({...m,returned:m.proposals.length}),...H(f)})})),e.get(`${t}/proposals/:id`,...n,v(async(s,i)=>{T(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){S(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){S(i,401,"Access link required","access_link_missing");return}try{let c=N(s,o),p=K(s.query),u;try{u=(await te(r.flowAdapter)).rev}catch{}let f=Te(r.proposalStore,l,d,c);i.json(ee(r.proposalStore,l,f,{...c,include:p},{...u?{liveRev:u}:{}}))}catch(c){let p=P(c,"get");if(p){i.status(p.statusCode).json({...p.body,hint:p.body.hint??"List proposals with GET \u2026/proposals."});return}_(i,c);return}})),wc(e,t,n,r,o),bc(e,t,n,r,o)}a(yc,"registerGetLinkProposalRoutes");function wc(e,t,n,r,o){e.get(`${t}/proposals/:id/withdraw`,...n,v((s,i)=>{T(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){S(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){S(i,401,"Access link required","access_link_missing");return}let c=s.query.confirm;if(c!=="1"&&c!=="true"){S(i,400,"Withdraw over GET requires confirm=1","confirm_required",X("confirm_required","get"));return}let p=typeof s.query.reason=="string"?s.query.reason:void 0,u=N(s,o),f=K(s.query);try{let m=yt(r.proposalStore,r.auditLog,l,d,p,u);i.json(ee(r.proposalStore,l,m,{...u,include:f}))}catch(m){let h=P(m,"get");if(h){let y=h.body.code;S(i,h.statusCode,ve[y]??"Request failed",y,X(y,"get"));return}S(i,500,"Internal server error","internal_error");return}}))}a(wc,"registerGetLinkWithdrawRoute");function bc(e,t,n,r,o){e.get(`${t}/proposals/:id/diff`,...n,v(async(s,i)=>{T(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){S(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){S(i,401,"Access link required","access_link_missing");return}try{let c=Te(r.proposalStore,l,d,N(s,o)),p=Tt(A(s.query,"format"));if(!p.ok){S(i,400,"Invalid diff format","invalid_diff_format","Valid formats: summary, full (default summary).");return}i.json(Pt(await Et(c,r.flowAdapter),p.format))}catch(c){let p=P(c);if(p){i.status(p.statusCode).json(p.body);return}_(i,c);return}}))}a(bc,"registerGetLinkDiffRoute");function vc(e,t,n,r,o,s){e.get(`${t}/draft/new`,...n,v(Ns(r,o.flowAdapter))),e.get(`${t}/draft/:draftId/append`,...n,v(Is(r))),e.get(`${t}/draft/:draftId/submit`,...n,v(Fs(r,o.proposalStore,o.auditLog,o.flowAdapter,s,o.nodeTypesAdapter)))}a(vc,"registerGetLinkDraftRoutes");$();var ei=require("module");function kc(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,ei.createRequire)(e)}a(kc,"createPluginRequire");function Ac(e){try{let r=kc()("@node-red/runtime")._?.adminApi?.httpAdmin;if(r&&typeof r.use=="function"&&r!==e.httpAdmin)return r}catch{}return null}a(Ac,"resolveEditorHttpAdmin");function ti(e){let t=[e.httpAdmin],n=Ac(e);return n&&t.push(n),t}a(ti,"httpAdminAppsForMcpErrors");var ni=Symbol.for("com55.ai-access.mcpJsonErrorHandler");function ri(e){return/\/mcp\/rpc\/?$/.test(e)||e.includes("/mcp/rpc")}a(ri,"isMcpRpcPath");function eo(e){return e.includes("/mcp")}a(eo,"isMcpPath");function Sc(e){return e.path||e.url||e.originalUrl||""}a(Sc,"requestPath");function xc(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(xc,"isJsonParseError");function Tc(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(Tc,"isPayloadTooLarge");function Pc(){return(e,t,n,r)=>{if(n.headersSent){r(e);return}let o=Sc(t);if(xc(e)&&eo(o)){if(ri(o)){n.status(400).json({jsonrpc:"2.0",error:{code:-32700,message:"Parse error"},id:null});return}n.status(400).json({error:"Invalid JSON body",code:"invalid_json"});return}if(Tc(e)&&eo(o)){n.status(413).json({error:`Request body exceeds limit of ${E} bytes`,code:"payload_too_large"});return}if(eo(o)){if(ri(o)){n.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:Dn(e).error,data:{code:"internal_error"}},id:null});return}n.status(500).json(Dn(e));return}r(e)}}a(Pc,"mcpJsonErrorHandler");function _c(e){let t=e;t[ni]||(t[ni]=!0,e.use(Pc()))}a(_c,"attachOnce");function oi(e){for(let t of ti(e))_c(t)}a(oi,"attachMcpJsonErrorHandlers");Vt();var Ve=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"}},Xe=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"}},tn=class extends Error{static{a(this,"AccessLinkValidationError")}code="access_link_invalid";statusCode=400;constructor(t){super(t),this.name="AccessLinkValidationError"}},De=class extends Error{static{a(this,"AccessLinkTtlError")}code="invalid_ttl";statusCode=400;hint=Ge;constructor(t){super(t),this.name="AccessLinkTtlError"}};var Ye=require("crypto");Vt();function to(e){return e==="get_link"||e==="rest"||e==="mcp"}a(to,"isAccessLinkKind");function no(e){return e??"get_link"}a(no,"resolveAccessLinkKind");function si(e){return(0,Ye.createHash)("sha256").update(e,"utf8").digest("hex")}a(si,"hashToken");function Rc(e,t){let n=Buffer.from(e,"utf8"),r=Buffer.from(t,"utf8");return n.length!==r.length?!1:(0,Ye.timingSafeEqual)(n,r)}a(Rc,"hashesEqual");function nn(e){let t={id:e.id,kind:no(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(nn,"toPublic");var Qe=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,Ye.randomBytes)(12).toString("hex")),this.randomToken=t.randomToken??(()=>`aia_${(0,Ye.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(nn)}create(t={}){let n=this.now();if([...this.links.values()].filter(u=>this.isActive(u,n)).length>=this.maxActiveLinks)throw new Ve(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 De("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=no(t.kind),d=qr(i);if(o>d)throw new De(Ur(i,d));let l=this.randomId(),c=this.randomToken(),p={id:l,tokenHash:si(c),token:c,kind:i,createdAtMs:n,expiresAtMs:n+o*1e3};return s!==void 0&&(p.label=s),t.createdBy!==void 0&&(p.createdBy=t.createdBy),this.links.set(l,p),{...nn(p),token:c}}peekToken(t){let n=this.links.get(t);return!n||!this.isActive(n,this.now())?null:n.token}revoke(t){let n=this.links.get(t);if(!n)throw new Xe(t);return n.revokedAtMs??=this.now(),nn(n)}getById(t){if(typeof t!="string"||t.length===0)return null;let n=this.links.get(t);return n?nn(n):null}validateResult(t){if(typeof t!="string"||t.length===0)return{status:"invalid"};let n=si(t),r=this.now();for(let o of this.links.values())if(Rc(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:nn(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){this.links.set(t.id,t)}};Vt();$e();Ae();function ro(e,t){return t}a(ro,"storeForRequest");function ai(e){let t=typeof e.user?.username=="string"?e.user.username:void 0;return t?{kind:"admin",username:t}:{kind:"admin"}}a(ai,"adminActor");function rr(e){let t=se(e.settings);return new Qe({defaultTtlSeconds:t.defaultLinkTtlSeconds,maxActiveLinks:t.maxActiveLinks})}a(rr,"createConfiguredLinkStore");function di(e,t){return(n,r)=>{let o=ro(e,t),s=o.list().map(i=>{let d=o.peekToken(i.id);return d?i.kind==="get_link"?{...i,url:gr(n,e.settings.httpAdminRoot,d)}:{...i,token:d}:i});r.json({links:s})}}a(di,"listLinksHandler");function li(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:Ge});return}i.ttlSeconds=s.ttlSeconds}if(typeof s.label=="string"&&(i.label=s.label),s.kind!==void 0){if(!to(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=ro(e,t).create(i),l=d.kind;if(n){let c=Date.parse(d.createdAt),p=Date.parse(d.expiresAt),u=Number.isFinite(c)&&Number.isFinite(p)&&p>c?Math.round((p-c)/1e3):void 0;n.append("link.created",ai(r),{linkId:d.id,label:d.label,expiresAt:d.expiresAt,createdAt:d.createdAt,kind:l,...u!==void 0?{ttlSeconds:u}:{}})}if(l==="get_link"){let c=gr(r,e.settings.httpAdminRoot,d.token);o.status(201).json({...d,url:c});return}o.status(201).json({...d})}catch(d){if(d instanceof Ve){o.status(d.statusCode).json({error:d.message,code:d.code});return}if(d instanceof De){o.status(d.statusCode).json({error:d.message,code:d.code,hint:d.hint});return}if(d instanceof tn){o.status(d.statusCode).json({error:d.message,code:d.code});return}_(o,d);return}}}a(li,"createLinkHandler");function ci(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=ro(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",ai(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 Xe){o.status(i.statusCode).json({error:i.message,code:i.code});return}_(o,i);return}}}a(ci,"revokeLinkHandler");function Ec(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(Ec,"ttlSecondsFromLink");function Lc(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=Ec(l);c!==void 0&&(n.ttlSeconds=c)}return{...e,details:n}}a(Lc,"enrichAuditLogEntry");function pi(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=>Lc(d,t));r.json({logs:i})}}a(pi,"listLogsHandler");var ui=require("@modelcontextprotocol/sdk/server/mcp.js"),fi=require("@modelcontextprotocol/sdk/server/streamableHttp.js"),w=require("zod");$();Me();D();be();Be();Ce();ce();function Cc(){return"0.1.0"}a(Cc,"pluginVersion");async function mi(e){try{return(await te(e)).rev}catch{return}}a(mi,"tryLiveRev");function B(e){return{content:[{type:"text",text:JSON.stringify(e)}]}}a(B,"jsonResult");function oo(e){return e===!0?new Set(["payload","baseFlow"]):new Set}a(oo,"includeSetFromPayloadFlag");function q(e,t=500){let n=Kt(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(q,"jsonError");var Nc=new Set(["list_proposals","list_my_proposals"]),Ic=new Set(["status","limit","offset","mine","flowId"]);function Fc(e){return!!e&&typeof e=="object"&&typeof e.safeParseAsync=="function"}a(Fc,"isZodLikeSchema");function Dc(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(Nc.has(e)&&Ic.has(s))return gt(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 g(d,"invalid_arguments",{details:{parameter:s,errors:i,hint:`Fix '${s}' to match the ${e} input schema (see tools/list).`}})}a(Dc,"mcpArgumentError");function Oc(e){let t=e,n=t.validateToolInput;typeof n=="function"&&(t.validateToolInput=async(r,o,s)=>{let i=r.inputSchema;if(!Fc(i))return n.call(e,r,o,s);let d=await i.safeParseAsync(o??{});if(d.success)return d.data;let l=Kt(Dc(s,o,d.error.issues),"mcp"),c=l?{...l.body,statusCode:l.statusCode}:{};throw new Error(JSON.stringify(c))})}a(Oc,"installMcpArgumentErrorMapping");function Mc(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 B(Lt(t,r))}catch(o){return q(o)}}),e.registerTool("list_flows",{description:"List all flows as a flat Admin API v2 array plus the current rev string. Returns { rev, flows }. Call this (or get_flow) before submit_proposal so expectedRev matches."},async()=>{try{return B(await te(n.flowAdapter))}catch(o){return q(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:w.z.string().min(1).describe("Flow / tab id"),summary:w.z.boolean().optional().describe("When true, return node list with sizes only (no field contents)")}},async({id:o,summary:s})=>{try{return B(s?await Hn(n.flowAdapter,o):await pe(n.flowAdapter,o))}catch(i){return q(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:w.z.string().min(1).describe("Node id"),flow:w.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,flow:s})=>{try{return B(await Bn(n.flowAdapter,o,s))}catch(i){return q(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:w.z.string().min(1).describe("Node id"),field:w.z.string().min(1).describe("Field name (e.g. template, func)"),lines:w.z.string().optional().describe("Optional 1-based line range A or A-B"),flow:w.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,field:s,lines:i,flow:d})=>{try{return B(await qn(n.flowAdapter,o,s,{...d?{flowId:d}:{},...i?{lines:i}:{}}))}catch(l){return q(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:w.z.string().describe("Substring to find (case-sensitive; non-empty required)"),flow:w.z.string().min(1).optional().describe("Optional tab id")}},async({q:o,flow:s})=>{try{return B(await Un(n.flowAdapter,o,s?{flowId:s}:{}))}catch(i){return q(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:w.z.string().optional().describe("Optional case-insensitive filter")}},async({q:o})=>{try{return B(await Sn(n.nodeTypesAdapter,o?{q:o}:{}))}catch(s){return q(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:w.z.string().min(1).describe("Node type id (e.g. inject, template) \u2014 case-sensitive"),includeHelp:w.z.boolean().optional().describe("When false, omit help sections (default true)")}},async({type:o,includeHelp:s})=>{try{return B(await He(n.nodeTypesAdapter,o,{includeHelp:s!==!1}))}catch(i){return q(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:w.z.string().describe("Search string (id / description / keywords; non-empty required)")}},async({q:o})=>{try{return B(await xn(n.nodeTypesAdapter,o,{...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{}}))}catch(s){return q(s,502)}})}a(Mc,"registerReadTools");function $c(e,t,n,r,o){let s=N(r,t.settings.httpAdminRoot);e.registerTool("submit_proposal",{description:`Submit a replace_flow proposal for owner approval (does not deploy). Preferred for small string edits: payload: { flowId, nodes: [{ op:"replace_text", node, field, find, replace, occurrence? }] } \u2014 find must be unique unless occurrence (1-based) is set; multiple replace_text ops apply in order; yields a short readable diff. Or send a full tab: payload.flow from get_flow (id, type, label?, nodes[], configs?). Inputs (all top-level, next to each other): type="replace_flow", expectedRev (required string from list_flows/get_flow rev; expected_rev alias accepted), optional title (\u226480 chars), and payload: { flowId, flow? } and/or payload.nodes / top-level nodes. Do not nest expectedRev inside payload. Payload size limit is ${E} bytes (413 if exceeded). expectedRev is checked against the live rev at submit time (409 if stale). replace_text errors: 400 text_not_found; 409 ambiguous_match with matchCount. Unknown palette types return 400 unknown_node_type with nearest matches \u2014 call list_node_types / search_palette and ask the user to install via Manage palette (never auto-install). Returns a lean proposal (id, status, title, storedTitle, flowLabel, displayName, reviewUrl, changeSummary, \u2026) by default; title may gain " in <flow>"; storedTitle is the title as saved (your title without that suffix, or the auto-generated title, which may already name the flow). Pass include_payload=true to include payload and baseFlow. When telling the user about the proposal, cite displayName and reviewUrl \u2014 not the raw id. Humans approve on the /mcp page (reviewUrl opens/scrolls to it).`,inputSchema:{type:w.z.literal("replace_flow").describe("Must be 'replace_flow'"),expectedRev:w.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:w.z.string().min(1).optional().describe("Alias for expectedRev (snake_case); prefer expectedRev"),title:w.z.string().max(80).optional().describe("Optional short human title (\u226480 chars). Auto-generated from changes when omitted."),include_payload:w.z.boolean().optional().describe("When true, include payload and baseFlow in the response (default lean)"),nodes:w.z.array(w.z.record(w.z.string(),w.z.unknown())).optional().describe("Optional node ops (set/add/remove/replace_text). Same as payload.nodes when flow is omitted."),payload:w.z.object({flowId:w.z.string().min(1).describe("Tab id to replace"),flow:w.z.object({id:w.z.string().optional(),type:w.z.string().optional().describe("Usually 'tab' (included by get_flow)"),label:w.z.string().optional(),nodes:w.z.array(w.z.record(w.z.string(),w.z.unknown())).optional().describe("Child nodes for the tab (from get_flow.nodes)"),configs:w.z.array(w.z.record(w.z.string(),w.z.unknown())).optional()}).passthrough().optional().describe("Full tab from get_flow; omit when using nodes ops"),nodes:w.z.array(w.z.record(w.z.string(),w.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 Ne(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={...Z(d,{...s,include:oo(i.include_payload)}),mine:!0},c=O(n.proposalStore,d.expectedRev,{flowId:d.payload.flowId}),p=we(ye(n.proposalStore,n.proposalStore.get(d.id),o.id));return B({...l,...p,...H(c)})}catch(d){return q(d)}}),Hc(e,n,o,s),Bc(e,n,o,s)}a($c,"registerProposalTools");function jc(e,t,n,r){let o={mine:w.z.boolean().optional().describe("true: only proposals created by this access link; false: only other links\u2019 proposals"),status:w.z.union([w.z.string(),w.z.array(w.z.string())]).optional().describe("Filter by proposal status: one value, an array, or a comma-separated string (pending, approved, rejected, failed, withdrawn, expired, superseded). An empty array or empty string means no status filter."),flowId:w.z.string().optional().describe("Filter by flow tab id (payload.flowId)"),limit:w.z.number().int().min(1).max(mt).optional().describe(`Page size (1\u2013${mt}). Omit to return all matches.`),offset:w.z.number().int().min(0).optional().describe("Skip this many filtered results (default 0)"),include_payload:w.z.boolean().optional().describe("When true, include payload and baseFlow on each proposal")},s=a(async i=>{try{let d=oo(i.include_payload),l=await mi(t.flowAdapter),c=l?O(t.proposalStore,l):0,p={};typeof i.mine=="boolean"&&(p.mine=i.mine);let u=Br(i.status);u&&(p.status=u),typeof i.flowId=="string"&&i.flowId.length>0&&(p.flowId=i.flowId),typeof i.limit=="number"&&(p.limit=i.limit),typeof i.offset=="number"&&(p.offset=i.offset);let f=ht(t.proposalStore,n,r,p);return B({proposals:f.proposals.map(m=>ee(t.proposalStore,n,m,{...r,include:d},{includeConflicts:!1})),...Ue({...f,returned:f.proposals.length}),...H(c)})}catch(d){return q(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(jc,"registerListProposalsTools");function Hc(e,t,n,r){jc(e,t,n,r),e.registerTool("get_proposal",{description:"Get any proposal by id (all tokens can read). Response includes mine:true|false and, for pending proposals, conflictingProposals when other pending work touches the same flow. Lean by default (no payload/baseFlow); pass include_payload=true for full bodies. Cite displayName + reviewUrl when talking to the user.",inputSchema:{id:w.z.string().min(1).describe("Proposal id (prop-\u2026)"),include_payload:w.z.boolean().optional().describe("When true, include payload and baseFlow")}},async({id:o,include_payload:s})=>{try{let i=await mi(t.flowAdapter),d=Te(t.proposalStore,n,o,r);return B(ee(t.proposalStore,n,d,{...r,include:oo(s)},{...i?{liveRev:i}:{}}))}catch(i){return q(i)}})}a(Hc,"registerProposalQueryTools");function Bc(e,t,n,r){e.registerTool("get_proposal_diff",{description:"Diff any proposal against its stored baseFlow snapshot (what changed). Optional format=summary|full (default summary). Pending + stale also includes vsCurrent.",inputSchema:{id:w.z.string().min(1).describe("Proposal id (prop-\u2026)"),format:w.z.enum(["summary","full"]).optional().describe("Diff payload format (default summary)")}},async({id:o,format:s})=>{try{let i=Te(t.proposalStore,n,o),d=Tt(s??"summary");if(!d.ok)return q(new g("Invalid diff format","invalid_diff_format",{details:{hint:"Valid formats: summary, full (default summary)."}}));let l=Pt(await Et(i,t.flowAdapter),d.format);return B(l)}catch(i){return q(i)}}),e.registerTool("withdraw_proposal",{description:"Withdraw one of YOUR pending proposals (status becomes withdrawn, not rejected). Optional reason. Only the creating access link can withdraw; other tokens get 403. Cite displayName + reviewUrl when telling the user.",inputSchema:{id:w.z.string().min(1).describe("Proposal id (prop-\u2026) owned by this access link"),reason:w.z.string().optional().describe("Optional withdraw reason")}},({id:o,reason:s})=>{try{let i=yt(t.proposalStore,t.auditLog,n,o,s,r);return B(ee(t.proposalStore,n,i,r))}catch(i){return q(i)}})}a(Bc,"registerProposalActionTools");function qc(e,t,n){let r=new ui.McpServer({name:"node-red-ai-access",version:Cc()});Oc(r);let o=n.accessLink;if(!o)throw new Error("Access link required");return Mc(r,e,t,n),$c(r,e,t,n,o),r}a(qc,"createAiMcpServer");function gi(e,t){return async(n,r,o)=>{try{if(!n.accessLink){r.setHeader("WWW-Authenticate",j),r.status(401).json({error:Q,code:ge});return}let s=qc(e,t,n),i=new fi.StreamableHTTPServerTransport({enableJsonResponse:!0});await s.connect(i),await i.handleRequest(n,r,n.body),r.on("close",()=>{i.close(),s.close()})}catch(s){if(!r.headersSent){_(r,s);return}o(s instanceof Error?s:new Error(String(s)))}}}a(gi,"mcpRpcPostHandler");function so(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(so,"mcpRpcMethodNotAllowed");var rn=require("fs"),Ct=require("path"),hi=require("url"),yi=require("yaml"),zc={};function Uc(){return typeof __dirname<"u"?__dirname:(0,Ct.dirname)((0,hi.fileURLToPath)(zc.url))}a(Uc,"moduleDir");function wi(){let e=Uc(),t=[(0,Ct.join)(e,"..","docs","api","openapi.yaml"),(0,Ct.join)(e,"..","..","docs","api","openapi.yaml"),(0,Ct.join)(process.cwd(),"docs","api","openapi.yaml")];for(let n of t)if((0,rn.existsSync)(n))return n;return null}a(wi,"resolveOpenApiYamlPath");function Gc(){let e=wi();if(!e)throw new Error("OpenAPI specification file was not found");return(0,rn.readFileSync)(e,"utf8")}a(Gc,"loadOpenApiYaml");function bi(){return(e,t)=>{try{let n=Gc();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(bi,"openApiYamlHandler");var io;function Wc(){let e=wi();if(!e)throw new Error("OpenAPI specification file was not found");return io?.path!==e&&(io={path:e,json:JSON.stringify((0,yi.parse)((0,rn.readFileSync)(e,"utf8")))}),io.json}a(Wc,"loadOpenApiJson");function vi(){return(e,t)=>{try{t.status(200).type("application/json").send(Wc())}catch(n){let r=n instanceof Error?n.message:String(n);t.status(500).json({error:r})}}}a(vi,"openApiJsonHandler");function Kc(){return`
|
|
929
|
+
`)){let f=0;for(;f<=i.length;){let m=i.indexOf(n,f);if(m<0)break;p(Ec(i,m),m),f=m+Math.max(1,n.length)}}if(d.length===0)continue;let u=d.length;r.push({flowId:t,nodeId:e.id,field:s,lines:d.slice(0,Sc),lineCount:u,snippet:Rc(i,Math.max(0,c),n.length)})}return r.length>=o}a(eo,"searchInNode");function Qs(e){return[...e.nodes??[],...e.configs??[]]}a(Qs,"nodePool");async function Lc(e,t,n,r,o){let s=await pe(e,t);for(let i of Qs(s.flow))if(eo(i,t,n,r,o))return{rev:s.rev,truncated:!0};return{rev:s.rev,truncated:!1}}a(Lc,"searchOneFlow");async function Cc(e,t,n,r){let o=await te(e),s=new Set;for(let i of Xs(o.flows))if(i.id)try{let d=await pe(e,i.id);for(let l of Qs(d.flow))if(s.add(l.id),eo(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(eo(d,l,t,n,r))return{rev:o.rev,truncated:!0}}return{rev:o.rev,truncated:!1}}a(Cc,"searchAllFlows");async function qn(e,t,n={}){if(typeof t!="string"||!t.trim())throw new g("q query parameter is required","missing_query");let r=typeof n.cap=="number"&&n.cap>0?Math.floor(n.cap):Ac,o=[],s=n.flowId?await Lc(e,n.flowId,t,o,r):await Cc(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(qn,"searchFlows");function Zs(e){if(e===void 0)return!1;let t=e.toLowerCase();return t==="1"||t==="true"||t==="yes"}a(Zs,"isTruthyQueryFlag");function Nc(e,t,n){switch(e){case"node_not_found":case"flow_not_found":case"field_not_found":return n??X(e,t);default:return X(e,t)}}a(Nc,"layeredReadErrorHint");function Je(e){return ve[e]??"Request failed"}a(Je,"safeErrorText");function ne(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(ne,"sendJson");function Un(e,t,n,r){let o=_(t,n);if(!o)return!1;let s=o.body.code,i={error:Je(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=Nc(s,n,r);return d&&(i.hint=d),ne(e,o.statusCode,i),!0}a(Un,"sendMappedError");function Gn(e){ne(e,502,{error:Je("upstream_error"),code:"upstream_error"})}a(Gn,"sendUpstreamError");function Wn(e,t="rest"){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){ne(r,400,{error:Je("missing_flow_id"),code:"missing_flow_id"});return}let s=Zs(A(n.query,"summary"));try{if(s){ne(r,200,await jn(e,o));return}ne(r,200,await pe(e,o))}catch(i){if(Un(r,i,t))return;Gn(r)}}}a(Wn,"getFlowOrSummaryHandler");function zn(e,t="rest"){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||!o){ne(r,400,{error:Je("missing_node_id"),code:"missing_node_id"});return}let s=A(n.query,"flow");try{ne(r,200,await Hn(e,o,s))}catch(i){if(Un(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;Gn(r)}}}a(zn,"getNodeHandler");function Jn(e,t="rest"){return async(n,r)=>{let o=n.params.id,s=n.params.name;if(typeof o!="string"||!o){ne(r,400,{error:Je("missing_node_id"),code:"missing_node_id"});return}if(typeof s!="string"||!s){ne(r,400,{error:Je("missing_field"),code:"missing_field"});return}let i=A(n.query,"flow"),d=A(n.query,"lines");try{ne(r,200,await Bn(e,o,s,{...i?{flowId:i}:{},...d?{lines:d}:{}}))}catch(l){if(Un(r,l,t,"Use summary/search to discover node ids and fields."))return;Gn(r)}}}a(Jn,"getNodeFieldHandler");function Kn(e,t="rest"){return async(n,r)=>{let o=A(n.query,"q");if(!o){ne(r,400,{error:Je("missing_query"),code:"missing_query",hint:t==="get"?"Example: GET \u2026/search?q=TODO&flow=tab1":X("missing_query",t)});return}let s=A(n.query,"flow");try{ne(r,200,await qn(e,o,s?{flowId:s}:{}))}catch(i){if(Un(r,i,t))return;Gn(r)}}}a(Kn,"searchFlowsHandler");O();Be();var Ic={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 Qt(e){return Ic[e]??"Request failed"}a(Qt,"safeErrorText");function Oe(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(Oe,"sendJson");function to(e,t){let n=_(t);if(!n)return!1;let r=n.body.code,o={error:Qt(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."),Oe(e,n.statusCode,o),!0}a(to,"sendMappedError");function Vn(e){return async(t,n)=>{try{let r=A(t.query,"q"),o=await An(e.nodeTypes,r?{q:r}:{});Oe(n,200,o)}catch(r){if(to(n,r))return;Oe(n,502,{error:Qt("upstream_error"),code:"upstream_error"})}}}a(Vn,"listNodeTypesHandler");function Xn(e){return async(t,n)=>{try{let r=t.params.type,o=typeof r=="string"?decodeURIComponent(r):"",s=A(t.query,"help"),i=!(s==="0"||s==="false"||s==="no"||s==="omit"),d=await He(e.nodeTypes,o,{includeHelp:i});Oe(n,200,d)}catch(r){if(to(n,r))return;Oe(n,502,{error:Qt("upstream_error"),code:"upstream_error"})}}}a(Xn,"getNodeTypeHandler");function Yn(e){return async(t,n)=>{try{let r=A(t.query,"q");if(!r){Oe(n,400,{error:Qt("missing_query"),code:"missing_query",hint:"Pass ?q= to search the Node-RED catalogue (recommend only)."});return}let o=await Sn(e.nodeTypes,r,{...e.catalogueFetcher?{catalogueFetcher:e.catalogueFetcher}:{},...e.catalogueUrl?{catalogueUrl:e.catalogueUrl}:{}});Oe(n,200,o)}catch(r){if(to(n,r))return;Oe(n,502,{error:Qt("upstream_error"),code:"upstream_error"})}}}a(Yn,"searchPaletteHandler");Ne();de();function Et(e,t){return e[t]??""}a(Et,"lineAt");function Zt(e,t,n){let r=e[t];return r?r[n]??0:0}a(Zt,"cell");function ei(e,t,n,r){let o=e[t];o&&(o[n]=r)}a(ei,"setCell");function ni(e,t){let n=ti(e),r=ti(t),o=n.length,s=r.length,i=[];for(let p=0;p<=o;p+=1){let u=[];for(let f=0;f<=s;f+=1)u.push(0);i.push(u)}for(let p=o-1;p>=0;p-=1)for(let u=s-1;u>=0;u-=1)Et(n,p)===Et(r,u)?ei(i,p,u,Zt(i,p+1,u+1)+1):ei(i,p,u,Math.max(Zt(i,p+1,u),Zt(i,p,u+1)));let d=[],l=0,c=0;for(;l<o&&c<s;){let p=Et(n,l),u=Et(r,c);p===u?(d.push({op:"equal",text:p}),l+=1,c+=1):Zt(i,l+1,c)>=Zt(i,l,c+1)?(d.push({op:"remove",text:p}),l+=1):(d.push({op:"add",text:u}),c+=1)}for(;l<o;)d.push({op:"remove",text:Et(n,l)}),l+=1;for(;c<s;)d.push({op:"add",text:Et(r,c)}),c+=1;return d}a(ni,"diffLines");function ti(e){return e.length===0?[]:e.split(`
|
|
930
|
+
`)}a(ti,"splitLines");ce();var si=new Set(["func","template","info","payload","code","text","format","syntax"]),Fc=new Set(["x","y","w","h","wires","g"]),Oc=new Set(["w","h","g"]);function Ke(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(Ke,"nodeName");function ri(e){let t=new Map;for(let n of e.nodes??[])t.set(n.id,n);for(let n of e.configs??[])t.set(n.id,n);return t}a(ri,"collectNodes");function er(e){if(e===void 0)return"undefined";if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(er).join(",")}]`;let t=e;return`{${Object.keys(t).sort().map(r=>`${JSON.stringify(r)}:${er(t[r])}`).join(",")}}`}a(er,"stableStringify");function ii(e,t){return er(e)===er(t)}a(ii,"valuesEqual");function tr(e){return typeof e=="string"?e:null}a(tr,"asText");function Dc(e){return/<\/?[a-z][\s\S]*>/i.test(e.slice(0,800))}a(Dc,"looksLikeHtml");function Mc(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:Dc(n)}a(Mc,"isHtmlTemplateField");function ai(e,t,n){let r=tr(n);if(si.has(t)&&r!==null){let o={key:t,kind:"text",value:r};return Mc(e,t,r)&&(o.preview="html"),o}return{key:t,kind:"scalar",value:n===void 0?null:n}}a(ai,"toPresenceProperty");function $c(e,t){let n=[];for(let r of[...Object.keys(e)].sort())if(!(Oc.has(r)||r==="id"||r==="z")){if(r==="wires"){let o=di(e,t);n.push({key:"wires",kind:"scalar",value:o.length>0?o:e.wires??null});continue}n.push(ai(e,r,e[r]))}return n}a($c,"nodeProperties");function di(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?Ke(l)??l.type:d})}r.push({port:s,to:i})}),r}a(di,"describeWiresStructured");var jc=new Set(["func","template","code","text"]);function Hc(e,t){let n=[],r=Ke(e);r&&n.push({key:"name",kind:"scalar",value:r});let o=di(e,t);o.length>0&&n.push({key:"wires",kind:"scalar",value:o});for(let s of jc){if(!(s in e))continue;let i=tr(e[s]);i===null||i.length===0||n.push(ai(e,s,i))}return n}a(Hc,"buildKeyFields");function Bc(e,t,n){let r=Ce(e.type),o=Ke(e),s=o?` "${o}"`:"",i=t?` in "${t}"`:"",d=n===1?"1 field":`${n} fields`;return`Changes ${r} node${s}${i} (${d})`}a(Bc,"buildModifiedSummary");function qc(e,t,n){let r=Ce(e.type),o=Ke(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 p=typeof e.field=="string"&&e.field?e.field:"";p&&(c+=`, output to msg.${p}`)}return c}a(qc,"buildPresenceSummary");function Qn(e,t,n,r){let o={id:e.id,type:e.type,summary:qc(e,t,n),keyFields:Hc(e,r),properties:$c(e,r)},s=Ke(e);return s!==void 0&&(o.name=s),o}a(Qn,"toPresenceNode");function Uc(e,t){let n=new Set([...Object.keys(e),...Object.keys(t)]),r=[];for(let o of[...n].sort()){if(Fc.has(o)||o==="id"||o==="z")continue;let s=e[o],i=t[o];if(ii(s,i))continue;let d=tr(s),l=tr(i);if(si.has(o)&&(d!==null||l!==null)){let c=d??"",p=l??"";r.push({key:o,kind:"text",before:c,after:p,lines:ni(c,p)});continue}r.push({key:o,kind:"scalar",before:s===void 0?null:s,after:i===void 0?null:i})}return r}a(Uc,"compareNodes");function Gc(e,t){let n=["label","disabled","info","env"],r=[];for(let o of n)ii(e[o],t[o])||r.push(o);return r}a(Gc,"compareTabMeta");function Zn(e){return typeof e.label=="string"&&e.label.trim()?e.label.trim():""}a(Zn,"flowLabelOf");function li(e,t){let n=ri(t),r=ri(e),o=Zn(e)||Zn(t),s=Zn(t)||Zn(e),i=[],d=[],l=[];for(let c of e.nodes??[])n.has(c.id)||i.push(Qn(c,"added",o,r));for(let c of e.configs??[])n.has(c.id)||i.push(Qn(c,"added",o,r));for(let c of t.nodes??[])r.has(c.id)||d.push(Qn(c,"removed",s,n));for(let c of t.configs??[])r.has(c.id)||d.push(Qn(c,"removed",s,n));for(let[c,p]of r){let u=n.get(c);if(!u)continue;let f=Uc(u,p);if(f.length>0){let m={id:c,type:p.type||u.type,summary:Bc(p,o||s,f.length),changes:f},h=Ke(p)??Ke(u);h!==void 0&&(m.name=h),l.push(m)}}return l.sort((c,p)=>c.id.localeCompare(p.id)),{summary:{added:i.length,removed:d.length,modified:l.length,tabChanges:Gc(t,e),beforeNodeCount:n.size,afterNodeCount:r.size},added:i,removed:d,modified:l}}a(li,"buildDiffSections");function oi(e,t,n={}){let r=li(e.payload.flow,t.flow),o=n.currentRev??t.rev,s=e.expectedRev,i=n.baseRevStale??(e.status==="pending"&&s!==o),d={proposalId:e.id,flowId:e.payload.flowId,expectedRev:s,currentRev:o,baseRevStale:i,compareAgainst:n.compareAgainst??"current",...r};return e.warnings&&e.warnings.length>0&&(d.warnings=e.warnings.map(l=>({...l}))),n.baseSnapshotMissing&&(d.baseSnapshotMissing=!0),n.vsCurrent&&(d.vsCurrent=n.vsCurrent),d}a(oi,"buildProposalDiff");async function Lt(e,t){let n=e.payload.flowId,r={id:n,type:"tab",nodes:[],configs:[]},o=e.expectedRev,s=r;try{let c=await t.getFlow(n);o=c.rev,s=c.flow}catch(c){if(!(c instanceof I))throw c;try{o=(await t.getFlows()).rev}catch{}}let d=e.status==="pending"&&e.expectedRev!==o,l=e.baseFlow;if(l!==void 0){let c=d?li(e.payload.flow,s):void 0;return oi(e,{rev:e.expectedRev,flow:l},{currentRev:o,compareAgainst:"base",baseRevStale:d,...c?{vsCurrent:c}:{}})}return oi(e,{rev:o,flow:s},{compareAgainst:"current",baseSnapshotMissing:!0,baseRevStale:d})}a(Lt,"computeProposalDiff");ce();Se();Ae();var ci=require("path");function nr(e){return(0,ci.join)(e.userDir,".ai-access")}a(nr,"getStorageDir");var Wc=["status:read","flows:read","proposals:create","proposals:read-own"];function pi(){return St()}a(pi,"readPackageVersion");function zc(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/1e3)):0}a(zc,"secondsRemainingUntil");function Jc(e,t=Date.now()){let n=Date.parse(e);return Number.isFinite(n)?Math.max(0,Math.ceil((n-t)/6e4)):0}a(Jc,"minutesRemainingUntil");function Kc(e,t){let n=me(t),r=Pe(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(Kc,"baseUrlsForTokenKind");function Ct(e,t){let n=ko(e,t),r=se(e.settings);if(t.accessLink){let o=Date.now();return{ok:!0,version:pi(),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:Jc(t.accessLink.expiresAt,o),secondsRemaining:zc(t.accessLink.expiresAt,o),permissions:[...Wc],baseUrls:Kc(t.accessLink.kind,e.settings.httpAdminRoot)}}}return{ok:!0,version:pi(),adminAuthEnabled:n.adminAuthEnabled,aiAccessEnabled:r.enabled,aiFeaturesEnabled:n.aiFeaturesEnabled,allowWithoutAdminAuth:n.allowWithoutAdminAuth,permission:n.permission,capabilities:n.capabilities,storageDir:nr(e.settings),defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,maxActiveLinks:r.maxActiveLinks,defaultDeploymentType:r.defaultDeploymentType,caller:{kind:"admin",...n.permission?{permission:n.permission}:{}}}}a(Ct,"buildStatus");function ui(e,t){let n=t.draftStore??new On,r=t.rateLimiters??$n(t.rateLimit??e.settings.aiAccess),o=e.httpAdmin,s="/mcp/a/:token",i=[r.ip,Bs(e,t.linkStore),r.token];o.use("/mcp/a",Ss()),o.get(s,...i,v((p,u)=>{T(u),u.setHeader("X-Content-Type-Options","nosniff");let f=me(e.settings.httpAdminRoot),m=p.accessLink;if(!m){S(u,401,"Access link required","access_link_missing");return}let h=Fs({link:m,openapiPath:`${f}/openapi.yaml`,restApiBase:f,mcpRpcPath:Pe(e.settings.httpAdminRoot),publicOrigin:hr(p)});u.status(200).type("text/markdown; charset=utf-8").send(h)})),o.get(`${s}/status`,...i,v((p,u)=>{T(u),u.json(Ct(e,p))})),o.get(`${s}/flows`,...i,v(async(p,u)=>{T(u);try{u.json(await te(t.flowAdapter))}catch(f){let m=_(f);if(m){u.status(m.statusCode).json({...m.body,hint:"Retry after the owner fixes the plugin runtime."});return}S(u,502,f instanceof Error?f.message:String(f),"upstream_error")}})),o.get(`${s}/flows/:id`,...i,v(async(p,u)=>{T(u),await Wn(t.flowAdapter,"get")(p,u)})),o.get(`${s}/nodes/:id`,...i,v(async(p,u)=>{T(u),await zn(t.flowAdapter,"get")(p,u)})),o.get(`${s}/nodes/:id/field/:name`,...i,v(async(p,u)=>{T(u),await Jn(t.flowAdapter,"get")(p,u)})),o.get(`${s}/search`,...i,v(async(p,u)=>{T(u),await Kn(t.flowAdapter,"get")(p,u)}));let d={nodeTypes:t.nodeTypesAdapter,...t.catalogueFetcher?{catalogueFetcher:t.catalogueFetcher}:{},...t.catalogueUrl?{catalogueUrl:t.catalogueUrl}:{}};o.get(`${s}/node-types`,...i,v(async(p,u)=>{T(u),await Vn(d)(p,u)})),o.get(`${s}/node-types/:type`,...i,v(async(p,u)=>{T(u),await Xn(d)(p,u)})),o.get(`${s}/palette/search`,...i,v(async(p,u)=>{T(u),await Yn(d)(p,u)}));let l=e.settings.httpAdminRoot;Vc(o,s,i,t,l),o.get(`${s}/propose`,...i,v(Ks(t.proposalStore,t.auditLog,t.flowAdapter,l,t.nodeTypesAdapter))),Qc(o,s,i,n,t,l);let c=Us();return o.all(`${s}/links`,c),o.all(`${s}/links/:id`,c),o.use("/mcp/a",qs()),n}a(ui,"registerGetLinkRoutes");function Vc(e,t,n,r,o){e.get(`${t}/proposals`,...n,v(async(s,i)=>{T(i);let d=s.accessLink;if(!d){S(i,401,"Access link required","access_link_missing");return}let l=N(s,o),c=K(s.query),p=Ln(s.query),u;try{u=(await te(r.flowAdapter)).rev}catch{}let f=u?D(r.proposalStore,u):0,m=ht(r.proposalStore,d,l,p);i.json({proposals:m.proposals.map(h=>ee(r.proposalStore,d,h,{...l,include:c},{includeConflicts:!1})),...Ue({...m,returned:m.proposals.length}),...H(f)})})),e.get(`${t}/proposals/:id`,...n,v(async(s,i)=>{T(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){S(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){S(i,401,"Access link required","access_link_missing");return}try{let c=N(s,o),p=K(s.query),u;try{u=(await te(r.flowAdapter)).rev}catch{}let f=_e(r.proposalStore,l,d,c);i.json(ee(r.proposalStore,l,f,{...c,include:p},{...u?{liveRev:u}:{}}))}catch(c){let p=_(c,"get");if(p){i.status(p.statusCode).json({...p.body,hint:p.body.hint??"List proposals with GET \u2026/proposals."});return}P(i,c);return}})),Xc(e,t,n,r,o),Yc(e,t,n,r,o)}a(Vc,"registerGetLinkProposalRoutes");function Xc(e,t,n,r,o){e.get(`${t}/proposals/:id/withdraw`,...n,v((s,i)=>{T(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){S(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){S(i,401,"Access link required","access_link_missing");return}let c=s.query.confirm;if(c!=="1"&&c!=="true"){S(i,400,"Withdraw over GET requires confirm=1","confirm_required",X("confirm_required","get"));return}let p=typeof s.query.reason=="string"?s.query.reason:void 0,u=N(s,o),f=K(s.query);try{let m=yt(r.proposalStore,r.auditLog,l,d,p,u);i.json(ee(r.proposalStore,l,m,{...u,include:f}))}catch(m){let h=_(m,"get");if(h){let y=h.body.code;S(i,h.statusCode,ve[y]??"Request failed",y,X(y,"get"));return}S(i,500,"Internal server error","internal_error");return}}))}a(Xc,"registerGetLinkWithdrawRoute");function Yc(e,t,n,r,o){e.get(`${t}/proposals/:id/diff`,...n,v(async(s,i)=>{T(i);let d=s.params.id,l=s.accessLink;if(typeof d!="string"||!d){S(i,400,"Missing proposal id","missing_proposal_id");return}if(!l){S(i,401,"Access link required","access_link_missing");return}try{let c=_e(r.proposalStore,l,d,N(s,o)),p=A(s.query,"format"),u=Tt(p==="json"||p==="md"?void 0:p);if(!u.ok){S(i,400,"Invalid diff format","invalid_diff_format","Valid formats: summary, full (default summary).");return}i.json(_t(await Lt(c,r.flowAdapter),u.format))}catch(c){let p=_(c);if(p){i.status(p.statusCode).json(p.body);return}P(i,c);return}}))}a(Yc,"registerGetLinkDiffRoute");function Qc(e,t,n,r,o,s){e.get(`${t}/draft/new`,...n,v(Ws(r,o.flowAdapter))),e.get(`${t}/draft/:draftId/append`,...n,v(zs(r))),e.get(`${t}/draft/:draftId/submit`,...n,v(Js(r,o.proposalStore,o.auditLog,o.flowAdapter,s,o.nodeTypesAdapter)))}a(Qc,"registerGetLinkDraftRoutes");$();var fi=require("module");function Zc(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,fi.createRequire)(e)}a(Zc,"createPluginRequire");function ep(e){try{let r=Zc()("@node-red/runtime")._?.adminApi?.httpAdmin;if(r&&typeof r.use=="function"&&r!==e.httpAdmin)return r}catch{}return null}a(ep,"resolveEditorHttpAdmin");function mi(e){let t=[e.httpAdmin],n=ep(e);return n&&t.push(n),t}a(mi,"httpAdminAppsForMcpErrors");var gi=Symbol.for("com55.ai-access.mcpJsonErrorHandler");function hi(e){return/\/mcp\/rpc\/?$/.test(e)||e.includes("/mcp/rpc")}a(hi,"isMcpRpcPath");function no(e){return e.includes("/mcp")}a(no,"isMcpPath");function tp(e){return e.path||e.url||e.originalUrl||""}a(tp,"requestPath");function np(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(np,"isJsonParseError");function rp(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(rp,"isPayloadTooLarge");function op(){return(e,t,n,r)=>{if(n.headersSent){r(e);return}let o=tp(t);if(np(e)&&no(o)){if(hi(o)){n.status(400).json({jsonrpc:"2.0",error:{code:-32700,message:"Parse error"},id:null});return}n.status(400).json({error:"Invalid JSON body",code:"invalid_json"});return}if(rp(e)&&no(o)){n.status(413).json({error:`Request body exceeds limit of ${E} bytes`,code:"payload_too_large"});return}if(no(o)){if(hi(o)){n.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:Fn(e).error,data:{code:"internal_error"}},id:null});return}n.status(500).json(Fn(e));return}r(e)}}a(op,"mcpJsonErrorHandler");function sp(e){let t=e;t[gi]||(t[gi]=!0,e.use(op()))}a(sp,"attachOnce");function yi(e){for(let t of mi(e))sp(t)}a(yi,"attachMcpJsonErrorHandlers");Xt();var Ve=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"}},Xe=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"}},en=class extends Error{static{a(this,"AccessLinkValidationError")}code="access_link_invalid";statusCode=400;constructor(t){super(t),this.name="AccessLinkValidationError"}},De=class extends Error{static{a(this,"AccessLinkTtlError")}code="invalid_ttl";statusCode=400;hint=Ge;constructor(t){super(t),this.name="AccessLinkTtlError"}};var Ye=require("crypto");Xt();function ro(e){return e==="get_link"||e==="rest"||e==="mcp"}a(ro,"isAccessLinkKind");function oo(e){return e??"get_link"}a(oo,"resolveAccessLinkKind");function wi(e){return(0,Ye.createHash)("sha256").update(e,"utf8").digest("hex")}a(wi,"hashToken");function ip(e,t){let n=Buffer.from(e,"utf8"),r=Buffer.from(t,"utf8");return n.length!==r.length?!1:(0,Ye.timingSafeEqual)(n,r)}a(ip,"hashesEqual");function tn(e){let t={id:e.id,kind:oo(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(tn,"toPublic");var Qe=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,Ye.randomBytes)(12).toString("hex")),this.randomToken=t.randomToken??(()=>`aia_${(0,Ye.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(tn)}create(t={}){let n=this.now();if([...this.links.values()].filter(u=>this.isActive(u,n)).length>=this.maxActiveLinks)throw new Ve(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 De("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=oo(t.kind),d=Gr(i);if(o>d)throw new De(Wr(i,d));let l=this.randomId(),c=this.randomToken(),p={id:l,tokenHash:wi(c),token:c,kind:i,createdAtMs:n,expiresAtMs:n+o*1e3};return s!==void 0&&(p.label=s),t.createdBy!==void 0&&(p.createdBy=t.createdBy),this.links.set(l,p),{...tn(p),token:c}}peekToken(t){let n=this.links.get(t);return!n||!this.isActive(n,this.now())?null:n.token}revoke(t){let n=this.links.get(t);if(!n)throw new Xe(t);return n.revokedAtMs??=this.now(),tn(n)}getById(t){if(typeof t!="string"||t.length===0)return null;let n=this.links.get(t);return n?tn(n):null}validateResult(t){if(typeof t!="string"||t.length===0)return{status:"invalid"};let n=wi(t),r=this.now();for(let o of this.links.values())if(ip(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:tn(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){this.links.set(t.id,t)}};Xt();xe();Ae();function so(e,t){return t}a(so,"storeForRequest");function vi(e){let t=typeof e.user?.username=="string"?e.user.username:void 0;return t?{kind:"admin",username:t}:{kind:"admin"}}a(vi,"adminActor");function rr(e){let t=se(e.settings);return new Qe({defaultTtlSeconds:t.defaultLinkTtlSeconds,maxActiveLinks:t.maxActiveLinks})}a(rr,"createConfiguredLinkStore");function ki(e,t){return(n,r)=>{let o=so(e,t),s=o.list().map(i=>{let d=o.peekToken(i.id);return d?i.kind==="get_link"?{...i,url:yr(n,e.settings.httpAdminRoot,d)}:{...i,token:d}:i});r.json({links:s})}}a(ki,"listLinksHandler");function Ai(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:Ge});return}i.ttlSeconds=s.ttlSeconds}if(typeof s.label=="string"&&(i.label=s.label),s.kind!==void 0){if(!ro(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=so(e,t).create(i),l=d.kind;if(n){let c=Date.parse(d.createdAt),p=Date.parse(d.expiresAt),u=Number.isFinite(c)&&Number.isFinite(p)&&p>c?Math.round((p-c)/1e3):void 0;n.append("link.created",vi(r),{linkId:d.id,label:d.label,expiresAt:d.expiresAt,createdAt:d.createdAt,kind:l,...u!==void 0?{ttlSeconds:u}:{}})}if(l==="get_link"){let c=yr(r,e.settings.httpAdminRoot,d.token);o.status(201).json({...d,url:c});return}o.status(201).json({...d})}catch(d){if(d instanceof Ve){o.status(d.statusCode).json({error:d.message,code:d.code});return}if(d instanceof De){o.status(d.statusCode).json({error:d.message,code:d.code,hint:d.hint});return}if(d instanceof en){o.status(d.statusCode).json({error:d.message,code:d.code});return}P(o,d);return}}}a(Ai,"createLinkHandler");function Si(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=so(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",vi(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 Xe){o.status(i.statusCode).json({error:i.message,code:i.code});return}P(o,i);return}}}a(Si,"revokeLinkHandler");function ap(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(ap,"ttlSecondsFromLink");function dp(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=ap(l);c!==void 0&&(n.ttlSeconds=c)}return{...e,details:n}}a(dp,"enrichAuditLogEntry");function xi(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=>dp(d,t));r.json({logs:i})}}a(xi,"listLogsHandler");var Ti=require("@modelcontextprotocol/sdk/server/mcp.js"),_i=require("@modelcontextprotocol/sdk/server/streamableHttp.js"),w=require("zod");$();$e();O();be();Be();Ne();ce();function lp(){return"0.1.1"}a(lp,"pluginVersion");async function Pi(e){try{return(await te(e)).rev}catch{return}}a(Pi,"tryLiveRev");function B(e){return{content:[{type:"text",text:JSON.stringify(e)}]}}a(B,"jsonResult");function io(e){return e===!0?new Set(["payload","baseFlow"]):new Set}a(io,"includeSetFromPayloadFlag");function q(e,t=500){let n=Vt(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(q,"jsonError");var cp=new Set(["list_proposals","list_my_proposals"]),pp=new Set(["status","limit","offset","mine","flowId"]);function up(e){return!!e&&typeof e=="object"&&typeof e.safeParseAsync=="function"}a(up,"isZodLikeSchema");function fp(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(cp.has(e)&&pp.has(s))return gt(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 g(d,"invalid_arguments",{details:{parameter:s,errors:i,hint:`Fix '${s}' to match the ${e} input schema (see tools/list).`}})}a(fp,"mcpArgumentError");function mp(e){let t=e,n=t.validateToolInput;typeof n=="function"&&(t.validateToolInput=async(r,o,s)=>{let i=r.inputSchema;if(!up(i))return n.call(e,r,o,s);let d=await i.safeParseAsync(o??{});if(d.success)return d.data;let l=Vt(fp(s,o,d.error.issues),"mcp"),c=l?{...l.body,statusCode:l.statusCode}:{};throw new Error(JSON.stringify(c))})}a(mp,"installMcpArgumentErrorMapping");function gp(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 B(Ct(t,r))}catch(o){return q(o)}}),e.registerTool("list_flows",{description:"List all flows as a flat Admin API v2 array plus the current rev string. Returns { rev, flows }. Call this (or get_flow) before submit_proposal so expectedRev matches."},async()=>{try{return B(await te(n.flowAdapter))}catch(o){return q(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:w.z.string().min(1).describe("Flow / tab id"),summary:w.z.boolean().optional().describe("When true, return node list with sizes only (no field contents)")}},async({id:o,summary:s})=>{try{return B(s?await jn(n.flowAdapter,o):await pe(n.flowAdapter,o))}catch(i){return q(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:w.z.string().min(1).describe("Node id"),flow:w.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,flow:s})=>{try{return B(await Hn(n.flowAdapter,o,s))}catch(i){return q(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:w.z.string().min(1).describe("Node id"),field:w.z.string().min(1).describe("Field name (e.g. template, func)"),lines:w.z.string().optional().describe("Optional 1-based line range A or A-B"),flow:w.z.string().min(1).optional().describe("Optional tab id to scope the lookup")}},async({id:o,field:s,lines:i,flow:d})=>{try{return B(await Bn(n.flowAdapter,o,s,{...d?{flowId:d}:{},...i?{lines:i}:{}}))}catch(l){return q(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:w.z.string().describe("Substring to find (case-sensitive; non-empty required)"),flow:w.z.string().min(1).optional().describe("Optional tab id")}},async({q:o,flow:s})=>{try{return B(await qn(n.flowAdapter,o,s?{flowId:s}:{}))}catch(i){return q(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:w.z.string().optional().describe("Optional case-insensitive filter")}},async({q:o})=>{try{return B(await An(n.nodeTypesAdapter,o?{q:o}:{}))}catch(s){return q(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:w.z.string().min(1).describe("Node type id (e.g. inject, template) \u2014 case-sensitive"),includeHelp:w.z.boolean().optional().describe("When false, omit help sections (default true)")}},async({type:o,includeHelp:s})=>{try{return B(await He(n.nodeTypesAdapter,o,{includeHelp:s!==!1}))}catch(i){return q(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:w.z.string().describe("Search string (id / description / keywords; non-empty required)")}},async({q:o})=>{try{return B(await Sn(n.nodeTypesAdapter,o,{...n.catalogueFetcher?{catalogueFetcher:n.catalogueFetcher}:{},...n.catalogueUrl?{catalogueUrl:n.catalogueUrl}:{}}))}catch(s){return q(s,502)}})}a(gp,"registerReadTools");function hp(e,t,n,r,o){let s=N(r,t.settings.httpAdminRoot);e.registerTool("submit_proposal",{description:`Submit a replace_flow proposal for owner approval (does not deploy). Preferred for small string edits: payload: { flowId, nodes: [{ op:"replace_text", node, field, find, replace, occurrence? }] } \u2014 find must be unique unless occurrence (1-based) is set; multiple replace_text ops apply in order; yields a short readable diff. Or send a full tab: payload.flow from get_flow (id, type, label?, nodes[], configs?). Inputs (all top-level, next to each other): type="replace_flow", expectedRev (required string from list_flows/get_flow rev; expected_rev alias accepted), optional title (\u226480 chars), and payload: { flowId, flow? } and/or payload.nodes / top-level nodes. Do not nest expectedRev inside payload. Payload size limit is ${E} bytes (413 if exceeded). expectedRev is checked against the live rev at submit time (409 if stale). replace_text errors: 400 text_not_found; 409 ambiguous_match with matchCount. Unknown palette types return 400 unknown_node_type with nearest matches \u2014 call list_node_types / search_palette and ask the user to install via Manage palette (never auto-install). Returns a lean proposal (id, status, title, storedTitle, flowLabel, displayName, reviewUrl, changeSummary, \u2026) by default; title may gain " in <flow>"; storedTitle is the title as saved (your title without that suffix, or the auto-generated title, which may already name the flow). Pass include_payload=true to include payload and baseFlow. When telling the user about the proposal, cite displayName and reviewUrl \u2014 not the raw id. Humans approve on the /mcp page (reviewUrl opens/scrolls to it).`,inputSchema:{type:w.z.literal("replace_flow").describe("Must be 'replace_flow'"),expectedRev:w.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:w.z.string().min(1).optional().describe("Alias for expectedRev (snake_case); prefer expectedRev"),title:w.z.string().max(80).optional().describe("Optional short human title (\u226480 chars). Auto-generated from changes when omitted."),include_payload:w.z.boolean().optional().describe("When true, include payload and baseFlow in the response (default lean)"),nodes:w.z.array(w.z.record(w.z.string(),w.z.unknown())).optional().describe("Optional node ops (set/add/remove/replace_text). Same as payload.nodes when flow is omitted."),payload:w.z.object({flowId:w.z.string().min(1).describe("Tab id to replace"),flow:w.z.object({id:w.z.string().optional(),type:w.z.string().optional().describe("Usually 'tab' (included by get_flow)"),label:w.z.string().optional(),nodes:w.z.array(w.z.record(w.z.string(),w.z.unknown())).optional().describe("Child nodes for the tab (from get_flow.nodes)"),configs:w.z.array(w.z.record(w.z.string(),w.z.unknown())).optional()}).passthrough().optional().describe("Full tab from get_flow; omit when using nodes ops"),nodes:w.z.array(w.z.record(w.z.string(),w.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 Ie(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={...Z(d,{...s,include:io(i.include_payload)}),mine:!0},c=D(n.proposalStore,d.expectedRev,{flowId:d.payload.flowId}),p=we(ye(n.proposalStore,n.proposalStore.get(d.id),o.id));return B({...l,...p,...H(c)})}catch(d){return q(d)}}),wp(e,n,o,s),bp(e,n,o,s)}a(hp,"registerProposalTools");function yp(e,t,n,r){let o={mine:w.z.boolean().optional().describe("true: only proposals created by this access link; false: only other links\u2019 proposals"),status:w.z.union([w.z.string(),w.z.array(w.z.string())]).optional().describe("Filter by proposal status: one value, an array, or a comma-separated string (pending, approved, rejected, failed, withdrawn, expired, superseded). An empty array or empty string means no status filter."),flowId:w.z.string().optional().describe("Filter by flow tab id (payload.flowId)"),limit:w.z.number().int().min(1).max(mt).optional().describe(`Page size (1\u2013${mt}). Omit to return all matches.`),offset:w.z.number().int().min(0).optional().describe("Skip this many filtered results (default 0)"),include_payload:w.z.boolean().optional().describe("When true, include payload and baseFlow on each proposal")},s=a(async i=>{try{let d=io(i.include_payload),l=await Pi(t.flowAdapter),c=l?D(t.proposalStore,l):0,p={};typeof i.mine=="boolean"&&(p.mine=i.mine);let u=Ur(i.status);u&&(p.status=u),typeof i.flowId=="string"&&i.flowId.length>0&&(p.flowId=i.flowId),typeof i.limit=="number"&&(p.limit=i.limit),typeof i.offset=="number"&&(p.offset=i.offset);let f=ht(t.proposalStore,n,r,p);return B({proposals:f.proposals.map(m=>ee(t.proposalStore,n,m,{...r,include:d},{includeConflicts:!1})),...Ue({...f,returned:f.proposals.length}),...H(c)})}catch(d){return q(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(yp,"registerListProposalsTools");function wp(e,t,n,r){yp(e,t,n,r),e.registerTool("get_proposal",{description:"Get any proposal by id (all tokens can read). Response includes mine:true|false and, for pending proposals, conflictingProposals when other pending work touches the same flow. Lean by default (no payload/baseFlow); pass include_payload=true for full bodies. Cite displayName + reviewUrl when talking to the user.",inputSchema:{id:w.z.string().min(1).describe("Proposal id (prop-\u2026)"),include_payload:w.z.boolean().optional().describe("When true, include payload and baseFlow")}},async({id:o,include_payload:s})=>{try{let i=await Pi(t.flowAdapter),d=_e(t.proposalStore,n,o,r);return B(ee(t.proposalStore,n,d,{...r,include:io(s)},{...i?{liveRev:i}:{}}))}catch(i){return q(i)}})}a(wp,"registerProposalQueryTools");function bp(e,t,n,r){e.registerTool("get_proposal_diff",{description:"Diff any proposal against its stored baseFlow snapshot (what changed). Optional format=summary|full (default summary). Pending + stale also includes vsCurrent.",inputSchema:{id:w.z.string().min(1).describe("Proposal id (prop-\u2026)"),format:w.z.enum(["summary","full"]).optional().describe("Diff payload format (default summary)")}},async({id:o,format:s})=>{try{let i=_e(t.proposalStore,n,o),d=Tt(s??"summary");if(!d.ok)return q(new g("Invalid diff format","invalid_diff_format",{details:{hint:"Valid formats: summary, full (default summary)."}}));let l=_t(await Lt(i,t.flowAdapter),d.format);return B(l)}catch(i){return q(i)}}),e.registerTool("withdraw_proposal",{description:"Withdraw one of YOUR pending proposals (status becomes withdrawn, not rejected). Optional reason. Only the creating access link can withdraw; other tokens get 403. Cite displayName + reviewUrl when telling the user.",inputSchema:{id:w.z.string().min(1).describe("Proposal id (prop-\u2026) owned by this access link"),reason:w.z.string().optional().describe("Optional withdraw reason")}},({id:o,reason:s})=>{try{let i=yt(t.proposalStore,t.auditLog,n,o,s,r);return B(ee(t.proposalStore,n,i,r))}catch(i){return q(i)}})}a(bp,"registerProposalActionTools");function vp(e,t,n){let r=new Ti.McpServer({name:"node-red-ai-access",version:lp()});mp(r);let o=n.accessLink;if(!o)throw new Error("Access link required");return gp(r,e,t,n),hp(r,e,t,n,o),r}a(vp,"createAiMcpServer");function Ri(e,t){return async(n,r,o)=>{try{if(!n.accessLink){r.setHeader("WWW-Authenticate",j),r.status(401).json({error:Q,code:ge});return}let s=vp(e,t,n),i=new _i.StreamableHTTPServerTransport({enableJsonResponse:!0});await s.connect(i),await i.handleRequest(n,r,n.body),r.on("close",()=>{i.close(),s.close()})}catch(s){if(!r.headersSent){P(r,s);return}o(s instanceof Error?s:new Error(String(s)))}}}a(Ri,"mcpRpcPostHandler");function ao(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(ao,"mcpRpcMethodNotAllowed");var nn=require("fs"),Nt=require("path"),Ei=require("url"),Li=require("yaml"),xp={};function kp(){return typeof __dirname<"u"?__dirname:(0,Nt.dirname)((0,Ei.fileURLToPath)(xp.url))}a(kp,"moduleDir");function Ci(){let e=kp(),t=[(0,Nt.join)(e,"..","docs","api","openapi.yaml"),(0,Nt.join)(e,"..","..","docs","api","openapi.yaml"),(0,Nt.join)(process.cwd(),"docs","api","openapi.yaml")];for(let n of t)if((0,nn.existsSync)(n))return n;return null}a(Ci,"resolveOpenApiYamlPath");function Ap(){let e=Ci();if(!e)throw new Error("OpenAPI specification file was not found");return(0,nn.readFileSync)(e,"utf8")}a(Ap,"loadOpenApiYaml");function Ni(){return(e,t)=>{try{let n=Ap();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(Ni,"openApiYamlHandler");var lo;function Sp(){let e=Ci();if(!e)throw new Error("OpenAPI specification file was not found");return lo?.path!==e&&(lo={path:e,json:JSON.stringify((0,Li.parse)((0,nn.readFileSync)(e,"utf8")))}),lo.json}a(Sp,"loadOpenApiJson");function Ii(){return(e,t)=>{try{t.status(200).type("application/json").send(Sp())}catch(n){let r=n instanceof Error?n.message:String(n);t.status(500).json({error:r})}}}a(Ii,"openApiJsonHandler");function _p(){return`
|
|
920
931
|
(function () {
|
|
921
932
|
var root = document.getElementById('mcp-root');
|
|
922
933
|
if (!root) return;
|
|
@@ -1137,7 +1148,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
1137
1148
|
}
|
|
1138
1149
|
return body;
|
|
1139
1150
|
}
|
|
1140
|
-
`}a(
|
|
1151
|
+
`}a(_p,"pageClientScriptPartA");function Pp(){return`
|
|
1141
1152
|
// Time helpers \u2014 keep in sync with page-time.ts (always browser-local Intl).
|
|
1142
1153
|
function titleFromDisplayName(displayName) {
|
|
1143
1154
|
var s = String(displayName || '');
|
|
@@ -1244,7 +1255,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
1244
1255
|
btn.disabled = false;
|
|
1245
1256
|
}, 1600);
|
|
1246
1257
|
}
|
|
1247
|
-
`}a(
|
|
1258
|
+
`}a(Pp,"pageClientScriptPartATimeCopy");function Rp(){return`
|
|
1248
1259
|
function shortId(value, keep) {
|
|
1249
1260
|
var s = String(value == null ? '' : value);
|
|
1250
1261
|
var n = keep || 8;
|
|
@@ -1374,7 +1385,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
1374
1385
|
}
|
|
1375
1386
|
return '';
|
|
1376
1387
|
}
|
|
1377
|
-
`}a(
|
|
1388
|
+
`}a(Rp,"pageClientScriptPartAFormat");function Ep(){return`
|
|
1378
1389
|
function lookupCachedProposal(id) {
|
|
1379
1390
|
if (!id) return null;
|
|
1380
1391
|
return cachedProposals.filter(function (x) { return x.id === id; })[0] || null;
|
|
@@ -1495,7 +1506,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
1495
1506
|
return k + '=' + String(v);
|
|
1496
1507
|
}).join(', '));
|
|
1497
1508
|
}
|
|
1498
|
-
`}a(
|
|
1509
|
+
`}a(Ep,"pageClientScriptPartAAudit");function Lp(){return`
|
|
1499
1510
|
// Hash helpers \u2014 keep in sync with page-hash.ts
|
|
1500
1511
|
function hashForTab(tab) {
|
|
1501
1512
|
if (tab === 'logs') return 'audit';
|
|
@@ -1777,7 +1788,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
1777
1788
|
}
|
|
1778
1789
|
}
|
|
1779
1790
|
|
|
1780
|
-
`}a(
|
|
1791
|
+
`}a(Lp,"pageClientScriptPartATabs");function Cp(){return`
|
|
1781
1792
|
async function loadLinks() {
|
|
1782
1793
|
if (!aiOn || !canManage) {
|
|
1783
1794
|
var dashLinksOff = $('dash-links');
|
|
@@ -1882,7 +1893,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
1882
1893
|
showToast(e.message || String(e), false);
|
|
1883
1894
|
}
|
|
1884
1895
|
}
|
|
1885
|
-
`}a(
|
|
1896
|
+
`}a(Cp,"pageClientScriptPartA2");function Np(){return`
|
|
1886
1897
|
|
|
1887
1898
|
function normalizeLinkKind(kind) {
|
|
1888
1899
|
if (kind === 'rest' || kind === 'mcp' || kind === 'get_link') return kind;
|
|
@@ -2208,6 +2219,11 @@ strings you will paste publicly beyond this temporary link.
|
|
|
2208
2219
|
var label = String(flowLabel || '').trim();
|
|
2209
2220
|
if (!label || !t) return t;
|
|
2210
2221
|
if (t.indexOf(label) >= 0) return t;
|
|
2222
|
+
var idx = t.lastIndexOf(' in ');
|
|
2223
|
+
if (idx >= 0) {
|
|
2224
|
+
var tail = t.slice(idx + 4);
|
|
2225
|
+
if (tail.length > 0 && label.indexOf(tail) === 0) return t;
|
|
2226
|
+
}
|
|
2211
2227
|
return t + ' in ' + label;
|
|
2212
2228
|
}
|
|
2213
2229
|
|
|
@@ -2460,7 +2476,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
2460
2476
|
decided: Math.max(decidedTotal, cachedProposals.filter(function (p) { return p.status !== 'pending'; }).length)
|
|
2461
2477
|
};
|
|
2462
2478
|
}
|
|
2463
|
-
`}a(
|
|
2479
|
+
`}a(Np,"pageClientScriptPartB");function Ip(){return`
|
|
2464
2480
|
var DIFF_MODE_KEY = 'ai-access-diff-mode';
|
|
2465
2481
|
var DIFF_SPLIT_MIN_PX = ${900};
|
|
2466
2482
|
var PRESENCE_SHORT_TEXT_MAX = ${120};
|
|
@@ -2577,7 +2593,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
2577
2593
|
'</div>';
|
|
2578
2594
|
}
|
|
2579
2595
|
|
|
2580
|
-
`}a(
|
|
2596
|
+
`}a(Ip,"pageClientScriptPartBDiff");function Fp(){return`
|
|
2581
2597
|
function isCollapsiblePresenceText(value) {
|
|
2582
2598
|
var s = String(value == null ? '' : value);
|
|
2583
2599
|
if (s.indexOf('\\n') >= 0) return true;
|
|
@@ -2678,7 +2694,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
2678
2694
|
});
|
|
2679
2695
|
});
|
|
2680
2696
|
}
|
|
2681
|
-
`}a(
|
|
2697
|
+
`}a(Fp,"pageClientScriptPartBDiffPresence");function Op(){return`
|
|
2682
2698
|
function textChangeLineCount(ch) {
|
|
2683
2699
|
return (ch.lines || []).length;
|
|
2684
2700
|
}
|
|
@@ -2737,7 +2753,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
2737
2753
|
'<code class="diff-before" title="before">' + esc(JSON.stringify(ch.before)) + '</code>' +
|
|
2738
2754
|
' \u2192 <code class="diff-after" title="after">' + esc(JSON.stringify(ch.after)) + '</code></div>';
|
|
2739
2755
|
}
|
|
2740
|
-
`}a(
|
|
2756
|
+
`}a(Op,"pageClientScriptPartBDiffView");function Dp(){return`
|
|
2741
2757
|
function renderDiffSections(sections, mode, emptyMsg) {
|
|
2742
2758
|
var parts = '';
|
|
2743
2759
|
if (sections.added && sections.added.length) {
|
|
@@ -2825,7 +2841,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
2825
2841
|
// Full view kept for mode-toggle re-render inside .proposal-changes.
|
|
2826
2842
|
return renderHumanDiff(diff, mode);
|
|
2827
2843
|
}
|
|
2828
|
-
`}a(
|
|
2844
|
+
`}a(Dp,"pageClientScriptPartBDiffView2");function Mp(){return`
|
|
2829
2845
|
function updatePendingBadges() {
|
|
2830
2846
|
var counts = countPendingBuckets();
|
|
2831
2847
|
var fullText = formatPendingBadgeText(counts.live, counts.stale);
|
|
@@ -2941,7 +2957,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
2941
2957
|
});
|
|
2942
2958
|
});
|
|
2943
2959
|
}
|
|
2944
|
-
`}a(
|
|
2960
|
+
`}a(Mp,"pageClientScriptPartB2");function $p(){return`
|
|
2945
2961
|
function renderDeleteBanner(diff) {
|
|
2946
2962
|
if (!isLargeDelete(diff)) return '';
|
|
2947
2963
|
var beforeN = diff.summary.beforeNodeCount || 0;
|
|
@@ -3023,7 +3039,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
3023
3039
|
loadProposals();
|
|
3024
3040
|
loadLogs();
|
|
3025
3041
|
}
|
|
3026
|
-
`}a(
|
|
3042
|
+
`}a($p,"pageClientScriptPartB2Actions");function jp(){return`
|
|
3027
3043
|
function renderRequestedVia(p) {
|
|
3028
3044
|
if (!p.createdBy) return '';
|
|
3029
3045
|
if (p.createdBy.label) {
|
|
@@ -3111,7 +3127,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
3111
3127
|
actions +
|
|
3112
3128
|
'</article>';
|
|
3113
3129
|
}
|
|
3114
|
-
`}a(
|
|
3130
|
+
`}a(jp,"pageClientScriptPartB2Cards");function Hp(){return`
|
|
3115
3131
|
function renderStaleItem(p) {
|
|
3116
3132
|
var cached = proposalDiffCache[p.id];
|
|
3117
3133
|
var title = titleForProposalCard(p);
|
|
@@ -3286,7 +3302,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
3286
3302
|
bindProposalActions(list);
|
|
3287
3303
|
focusProposalFromHash();
|
|
3288
3304
|
}
|
|
3289
|
-
`}a(
|
|
3305
|
+
`}a(Hp,"pageClientScriptPartB2CardsList");function Bp(){return`
|
|
3290
3306
|
async function refreshFlowLabels() {
|
|
3291
3307
|
try {
|
|
3292
3308
|
var data = await api('/flows');
|
|
@@ -3453,7 +3469,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
3453
3469
|
replaceLocationHash('#proposals');
|
|
3454
3470
|
return true;
|
|
3455
3471
|
}
|
|
3456
|
-
`}a(
|
|
3472
|
+
`}a(Bp,"pageClientScriptPartB2Focus");function qp(){return`
|
|
3457
3473
|
function bindDiffModeToggle(host, diff) {
|
|
3458
3474
|
host.querySelectorAll('[data-diff-mode]').forEach(function (btn) {
|
|
3459
3475
|
btn.addEventListener('click', function () {
|
|
@@ -3565,7 +3581,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
3565
3581
|
esc(e.message || String(e)) + '</p>';
|
|
3566
3582
|
}
|
|
3567
3583
|
}
|
|
3568
|
-
}`}a(
|
|
3584
|
+
}`}a(qp,"pageClientScriptPartB2Apply");function Up(){return`
|
|
3569
3585
|
|
|
3570
3586
|
function dialogFocusables(dialog) {
|
|
3571
3587
|
if (!dialog) return [];
|
|
@@ -3879,7 +3895,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
3879
3895
|
throw e;
|
|
3880
3896
|
}
|
|
3881
3897
|
}
|
|
3882
|
-
`}a(
|
|
3898
|
+
`}a(Up,"pageClientScriptPartC");function Gp(){return`
|
|
3883
3899
|
var cachedLogs = [];
|
|
3884
3900
|
|
|
3885
3901
|
function renderLogsTable(logs) {
|
|
@@ -3956,7 +3972,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
3956
3972
|
showFlash(flash, e.message || String(e), false);
|
|
3957
3973
|
}
|
|
3958
3974
|
}
|
|
3959
|
-
`}a(
|
|
3975
|
+
`}a(Gp,"pageClientScriptPartCLogs");function Wp(){return`
|
|
3960
3976
|
function applyStatus(s) {
|
|
3961
3977
|
aiOn = !!s.aiFeaturesEnabled;
|
|
3962
3978
|
canManage = !!(s.capabilities && s.capabilities.manageLinks);
|
|
@@ -4117,7 +4133,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
4117
4133
|
});
|
|
4118
4134
|
}
|
|
4119
4135
|
}
|
|
4120
|
-
`}a(
|
|
4136
|
+
`}a(Wp,"pageClientScriptPartD");function zp(){return`
|
|
4121
4137
|
async function boot() {
|
|
4122
4138
|
accessToken = readEditorToken();
|
|
4123
4139
|
if (!accessToken) {
|
|
@@ -4219,7 +4235,7 @@ strings you will paste publicly beyond this temporary link.
|
|
|
4219
4235
|
|
|
4220
4236
|
boot();
|
|
4221
4237
|
})();
|
|
4222
|
-
`}a(
|
|
4238
|
+
`}a(zp,"pageClientScriptPartDBoot");function Fi(){return(_p()+Pp()+Rp()+Ep()+Lp()+Cp()+Np()+Ip()+Fp()+Op()+Dp()+Mp()+$p()+jp()+Hp()+Bp()+qp()+Up()+Gp()+Wp()+zp()).trim()}a(Fi,"pageClientScript");function Jp(){return`
|
|
4223
4239
|
:root {
|
|
4224
4240
|
color-scheme: light;
|
|
4225
4241
|
--bg: #eef2f5;
|
|
@@ -4287,7 +4303,7 @@ h1 {
|
|
|
4287
4303
|
text-decoration: none;
|
|
4288
4304
|
text-align: center;
|
|
4289
4305
|
}
|
|
4290
|
-
`}a(
|
|
4306
|
+
`}a(Jp,"pageStylesPartA");function Kp(){return`
|
|
4291
4307
|
nav.tabs {
|
|
4292
4308
|
position: sticky;
|
|
4293
4309
|
top: 0;
|
|
@@ -4381,7 +4397,7 @@ dd {
|
|
|
4381
4397
|
padding: 0.22rem 0.55rem;
|
|
4382
4398
|
border-radius: 6px;
|
|
4383
4399
|
cursor: help;
|
|
4384
|
-
background: var`}a(
|
|
4400
|
+
background: var`}a(Kp,"pageStylesPartA2");function Vp(){return`(--accent-soft);
|
|
4385
4401
|
color: var(--accent);
|
|
4386
4402
|
font-size: 0.82rem;
|
|
4387
4403
|
font-weight: 650;
|
|
@@ -4542,7 +4558,7 @@ th { color: var(--muted); font-weight: 600; }
|
|
|
4542
4558
|
color: var(--muted);
|
|
4543
4559
|
font-size: 0.92rem;
|
|
4544
4560
|
}
|
|
4545
|
-
`}a(
|
|
4561
|
+
`}a(Vp,"pageStylesPartB");function Xp(){return`
|
|
4546
4562
|
.logs-toolbar { margin-bottom: 0.65rem; }
|
|
4547
4563
|
table.audit-table {
|
|
4548
4564
|
table-layout: fixed;
|
|
@@ -4618,7 +4634,7 @@ table.audit-table {
|
|
|
4618
4634
|
cursor: help;
|
|
4619
4635
|
border-bottom: 1px dotted var(--line);
|
|
4620
4636
|
}
|
|
4621
|
-
`}a(
|
|
4637
|
+
`}a(Xp,"pageStylesPartC");function Yp(){return`
|
|
4622
4638
|
.proposal-diff {
|
|
4623
4639
|
margin: 0.65rem 0;
|
|
4624
4640
|
padding: 0.55rem 0.65rem;
|
|
@@ -4695,7 +4711,7 @@ table.audit-table {
|
|
|
4695
4711
|
color: var(--muted);
|
|
4696
4712
|
margin-bottom: 0.15rem;
|
|
4697
4713
|
}
|
|
4698
|
-
`}a(
|
|
4714
|
+
`}a(Yp,"pageStylesPartD");function Qp(){return`
|
|
4699
4715
|
.diff-block {
|
|
4700
4716
|
margin-top: 0.25rem;
|
|
4701
4717
|
font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
|
|
@@ -4777,7 +4793,7 @@ table.audit-table {
|
|
|
4777
4793
|
}
|
|
4778
4794
|
.diff-before { color: var(--danger); word-break: break-word; }
|
|
4779
4795
|
.diff-after { color: var(--ok); word-break: break-word; }
|
|
4780
|
-
`}a(
|
|
4796
|
+
`}a(Qp,"pageStylesPartE");function Zp(){return`
|
|
4781
4797
|
.proposal-detail {
|
|
4782
4798
|
scroll-margin-top: 4.5rem;
|
|
4783
4799
|
}
|
|
@@ -4903,7 +4919,7 @@ table.audit-table {
|
|
|
4903
4919
|
.audit-table .col-action { width: 5.5rem; min-width: 5.5rem; }
|
|
4904
4920
|
.audit-table .col-actor { width: 7rem; min-width: 6rem; }
|
|
4905
4921
|
}
|
|
4906
|
-
`}a(
|
|
4922
|
+
`}a(Zp,"pageStylesPartF");function eu(){return`
|
|
4907
4923
|
.dash-grid {
|
|
4908
4924
|
display: grid;
|
|
4909
4925
|
grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
|
|
@@ -5120,7 +5136,7 @@ button.id-copy:focus {
|
|
|
5120
5136
|
background: var(--code-bg, #f6f8fa);
|
|
5121
5137
|
border-radius: 4px;
|
|
5122
5138
|
}
|
|
5123
|
-
`}a(
|
|
5139
|
+
`}a(eu,"pageStylesPartG");function tu(){return`
|
|
5124
5140
|
.stale-group {
|
|
5125
5141
|
margin: 1rem 0 0;
|
|
5126
5142
|
border: 1px solid var(--warn-border);
|
|
@@ -5284,7 +5300,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
5284
5300
|
margin-left: auto;
|
|
5285
5301
|
}
|
|
5286
5302
|
.debug-toggle input { width: auto; }
|
|
5287
|
-
`}a(
|
|
5303
|
+
`}a(tu,"pageStylesPartH");function Oi(){return(Jp()+Kp()+Vp()+Xp()+Yp()+Qp()+Zp()+eu()+tu()).trim()}a(Oi,"pageStyles");function nu(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""")}a(nu,"escapeHtml");function ru(){return`
|
|
5288
5304
|
<header class="page-header">
|
|
5289
5305
|
<h1>Node-RED AI Access</h1>
|
|
5290
5306
|
<p class="lede">
|
|
@@ -5313,7 +5329,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
5313
5329
|
Proposals <span class="tab-badge" id="proposals-pending-badge" hidden>0</span>
|
|
5314
5330
|
</button>
|
|
5315
5331
|
<button type="button" data-tab="logs" aria-selected="false">Audit log</button>
|
|
5316
|
-
</nav>`}a(
|
|
5332
|
+
</nav>`}a(ru,"renderMcpPageHeader");function ou(){return`
|
|
5317
5333
|
<section class="panel active" id="section-status" aria-label="Status" hidden>
|
|
5318
5334
|
<h2>Status</h2>
|
|
5319
5335
|
<div id="status-flash" class="flash" hidden></div>
|
|
@@ -5357,7 +5373,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
5357
5373
|
<div class="row" style="margin-top:1rem">
|
|
5358
5374
|
<button type="button" class="action secondary" id="refresh-status">Refresh</button>
|
|
5359
5375
|
</div>
|
|
5360
|
-
</section>`}a(
|
|
5376
|
+
</section>`}a(ou,"renderMcpPageStatusSection");function su(){return`
|
|
5361
5377
|
<section class="panel" id="section-links" aria-label="Access links" hidden>
|
|
5362
5378
|
<h2>Access links</h2>
|
|
5363
5379
|
<p class="muted" id="links-intro">
|
|
@@ -5368,7 +5384,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
5368
5384
|
<div id="links-flash" class="flash" hidden></div>
|
|
5369
5385
|
<div id="links-manage"></div>
|
|
5370
5386
|
<div id="links-list"></div>
|
|
5371
|
-
</section>`}a(
|
|
5387
|
+
</section>`}a(su,"renderMcpPageLinksSection");function iu(){return`
|
|
5372
5388
|
<section class="panel" id="section-proposals" aria-label="Proposals" hidden>
|
|
5373
5389
|
<h2>Proposals</h2>
|
|
5374
5390
|
<p id="proposals-hint" class="muted"></p>
|
|
@@ -5386,7 +5402,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
5386
5402
|
</div>
|
|
5387
5403
|
<div id="proposals-flash" class="flash" hidden></div>
|
|
5388
5404
|
<div id="proposals-list"><p class="muted">Loading\u2026</p></div>
|
|
5389
|
-
</section>`}a(
|
|
5405
|
+
</section>`}a(iu,"renderMcpPageProposalsSection");function au(){return`
|
|
5390
5406
|
<section class="panel" id="section-logs" aria-label="Audit log" hidden>
|
|
5391
5407
|
<h2>Audit log</h2>
|
|
5392
5408
|
<div id="logs-flash" class="flash" hidden></div>
|
|
@@ -5402,7 +5418,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
5402
5418
|
</label>
|
|
5403
5419
|
</div>
|
|
5404
5420
|
<div id="logs-list"><p class="muted">Loading\u2026</p></div>
|
|
5405
|
-
</section>`}a(
|
|
5421
|
+
</section>`}a(au,"renderMcpPageLogsSection");function du(){return`
|
|
5406
5422
|
<dialog id="reject-dialog">
|
|
5407
5423
|
<form method="dialog" id="reject-form">
|
|
5408
5424
|
<h3>Reject proposal</h3>
|
|
@@ -5444,7 +5460,7 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
5444
5460
|
<button type="submit" value="confirm" class="action danger" id="confirm-action-btn">Confirm</button>
|
|
5445
5461
|
</div>
|
|
5446
5462
|
</form>
|
|
5447
|
-
</dialog>`}a(
|
|
5463
|
+
</dialog>`}a(du,"renderMcpPageDialogs");function Di(e){let t={apiBase:e.apiBase,mcpRpc:e.mcpRpc??"/mcp/rpc",editorRoot:e.editorRoot,authTokenKey:e.authTokenKey};return`<!DOCTYPE html>
|
|
5448
5464
|
<html lang="en">
|
|
5449
5465
|
<head>
|
|
5450
5466
|
<meta charset="utf-8" />
|
|
@@ -5452,20 +5468,20 @@ dialog h3 { margin: 0 0 0.4rem; }
|
|
|
5452
5468
|
<meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" />
|
|
5453
5469
|
<meta http-equiv="Pragma" content="no-cache" />
|
|
5454
5470
|
<title>Node-RED AI Access</title>
|
|
5455
|
-
<style>${
|
|
5471
|
+
<style>${Oi()}</style>
|
|
5456
5472
|
</head>
|
|
5457
5473
|
<body>
|
|
5458
5474
|
<main
|
|
5459
5475
|
id="mcp-root"
|
|
5460
|
-
data-config="${
|
|
5461
|
-
>${
|
|
5462
|
-
${
|
|
5463
|
-
${
|
|
5464
|
-
${
|
|
5465
|
-
${
|
|
5466
|
-
${
|
|
5476
|
+
data-config="${nu(JSON.stringify(t))}"
|
|
5477
|
+
>${ru()}
|
|
5478
|
+
${ou()}
|
|
5479
|
+
${su()}
|
|
5480
|
+
${iu()}
|
|
5481
|
+
${au()}
|
|
5482
|
+
${du()}
|
|
5467
5483
|
</main>
|
|
5468
|
-
<script>${
|
|
5484
|
+
<script>${Fi()}</script>
|
|
5469
5485
|
</body>
|
|
5470
|
-
</html>`}a(Si,"renderMcpPage");ce();de();dt();$();Me();D();be();Ce();ce();function Ze(e,t,n,r={}){e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("Content-Type","application/json; charset=utf-8");let o={error:ve[n]??"Request failed",code:n,...r},s=X(n,"rest");s&&typeof o.hint!="string"&&(o.hint=s),e.status(t).json(o)}a(Ze,"sendSafeJsonError");function xi(e){let t={kind:"admin"};return typeof e.user?.username=="string"&&(t.username=e.user.username),t}a(xi,"adminActor");function Ti(e,t,n,r,o){return async(s,i)=>{if(!s.accessLink){i.setHeader("WWW-Authenticate",j),i.status(401).json({error:Q,code:ge});return}try{let d=N(s,r),l=K(s.query),c=await Ne(e,t,s.accessLink,s.body??{},n,d,o),p=O(e,c.expectedRev,{flowId:c.payload.flowId}),u=ye(e,e.get(c.id),s.accessLink.id),f=c.warnings;i.status(201).json({...Z(c,{...d,include:l}),mine:!0,...we(u),...H(p),...f&&f.length>0?{warnings:f}:{}})}catch(d){let l=P(d,"rest");if(l){i.status(l.statusCode).json(l.body);return}_(i,d);return}}}a(Ti,"createProposalHandler");function Pi(e,t,n){return async(r,o)=>{let s,i;try{let k=await t.getFlows();s=k.rev,i=Tn(k.flows)}catch{}let d={...N(r,n),...i?{liveFlowLabels:i}:{}},l=K(r.query),c;try{c=Cn(r.query)}catch(k){_(o,k);return}let p=s?O(e,s):0,u=r.accessLink;if(u){let k=ht(e,u,d,c),re=k.proposals.map(U=>{let L=ee(e,u,U,{...d,include:l},{includeConflicts:!1});return L.status!=="pending"||s===void 0?L:{...L,baseRevStale:L.expectedRev!==s}});o.json({proposals:re,...Ue({...k,returned:k.proposals.length}),...H(p)});return}let f={};c.flowId&&(f.flowId=c.flowId);let m=Yo(e.list(f).filter(k=>jr(k.status,c.status)),{...d,include:l}),h=c.offset??0,x=(c.limit!==void 0?m.slice(h,h+c.limit):h>0?m.slice(h):m).map(k=>k.status!=="pending"||s===void 0?k:{...k,baseRevStale:k.expectedRev!==s});o.json({proposals:x,...Ue({total:m.length,limit:c.limit??null,offset:h,returned:x.length}),...H(p)})}}a(Pi,"listProposalsHandler");function _i(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=N(r,t),d=K(r.query),l;if(n)try{l=(await n.getFlows()).rev}catch{}try{if(r.accessLink){let c=Te(e,r.accessLink,s,i);o.json(ee(e,r.accessLink,c,{...i,include:d},{...l?{liveRev:l}:{}}));return}l&&O(e,l),o.json(Z(e.get(s),{...i,include:d}))}catch(c){if(c instanceof J){Ze(o,404,c.code);return}let p=P(c,"rest");if(p){o.status(p.statusCode).json(p.body);return}_(o,c);return}}}a(_i,"getProposalHandler");function Ri(e,t){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){r.status(400).json({error:"Missing proposal id"});return}let s=n.body??{},i={};if(s.deploymentType!==void 0){if(s.deploymentType!=="nodes"&&s.deploymentType!=="flows"&&s.deploymentType!=="full"){r.status(400).json({error:"deploymentType must be 'nodes', 'flows', or 'full'"});return}i.deploymentType=s.deploymentType}try{let d=await e.approve(o,xi(n),i);r.json(V(d,N(n,t)))}catch(d){if(d instanceof J||d instanceof G||d instanceof Bt||d instanceof M){Ze(r,d.statusCode,d.code);return}_(r,d);return}}}a(Ri,"approveProposalHandler");function Ei(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=V(e.reject(o,xi(n),i),N(n,t));r.json(d)}catch(d){if(d instanceof J||d instanceof G||d instanceof M){Ze(r,d.statusCode,d.code);return}_(r,d);return}}}a(Ei,"rejectProposalHandler");function Li(e,t,n){return(r,o)=>{if(!r.accessLink){o.setHeader("WWW-Authenticate",j),o.status(401).json({error:Q,code:ge});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=N(r,n),l=K(r.query);try{let c=yt(e,t,r.accessLink,s,i.reason,d);o.json(ee(e,r.accessLink,c,{...d,include:l}))}catch(c){let p=P(c,"rest");if(p){Ze(o,p.statusCode,p.body.code);return}Ze(o,500,"internal_error");return}}}a(Li,"withdrawProposalHandler");function Ci(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?Te(e,n.accessLink,o):e.get(o),i=Tt(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=Pt(await Et(s,t),i.format);r.json(d)}catch(s){if(s instanceof J){Ze(r,404,s.code);return}if(s instanceof I||s instanceof ae||s instanceof he){Ze(r,s.statusCode,s.code);return}let i=P(s,"rest");if(i){r.status(i.statusCode).json(i.body);return}_(r,s);return}}}a(Ci,"getProposalDiffHandler");de();de();kn();function Cp(e){return e.endsWith("/")?e.slice(0,-1):e}a(Cp,"trimTrailingSlash");var or=class{static{a(this,"HttpAdapter")}kind="http";baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=Cp(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 I(n):t.status===409?new F("unknown","unknown",`Admin API ${n}`):t.status===401||t.status===403?new he(hn):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 qt(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 I(t);let s=await this.readJson(r,"GET /flows"),i=await this.readJson(o,`GET /flow/${t}`);return{rev:qt(s,"GET /flows").rev,flow:lt(t,i)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new F(t.expectedRev,r.rev,"updateFlow");let o=vn(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 F(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}}};de();kn();function Np(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(Np,"requireFlowsApi");function Ip(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(Ip,"resolveRev");var sr=class{static{a(this,"RedAdapter")}kind="red";flows;constructor(t){this.flows=Np(t)}async getFlows(){let t=await this.flows.getFlows({});return qt(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 Fp(o)?new I(t):o}return{rev:n.rev,flow:lt(t,r)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new F(t.expectedRev,r.rev,"updateFlow");let o=vn(r.flows,t.flowId,t.flow),s=await this.flows.setFlows({flows:{flows:o,rev:t.expectedRev},deploymentType:n});return{flowId:t.flowId,rev:Ip(s)}}};function Ni(e){let t=e?.runtime?.flows;return!!(t?.getFlows&&t.getFlow&&t.setFlows)}a(Ni,"canUseRedRuntime");function Fp(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(Fp,"isRuntimeFlowNotFound");var Ii=require("module");function Fi(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(Fi,"isFlowsApi");function Dp(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,Ii.createRequire)(e)}a(Dp,"createPluginRequire");function Op(e){try{let t=e("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}a(Op,"isNodeRedRuntimeInitialized");function Mp(){try{let e=Dp();if(!Op(e))return null;let t=e("@node-red/runtime");return Fi(t.flows)?t.flows:null}catch{return null}}a(Mp,"tryRequireInitializedRuntimeFlows");function Di(e){if(e?.runtime&&Fi(e.runtime.flows))return e;let t=Mp();return t?{runtime:{flows:t}}:null}a(Di,"resolveRuntimeHost");function Oi(e={}){let t=Di(e.RED??null);if(t&&Ni(t))return new sr(t);let n=e.adminAuthEnabled===!0;if(e.http?.baseUrl){if(n&&!e.http.accessToken)throw new ae("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 or(e.http)}throw new ae("No usable Node-RED flow adapter: in-process flows API is unavailable and no HTTP Admin API baseUrl was provided")}a(Oi,"createFlowAdapter");de();var ar=require("fs"),Mi=require("path");wn();$();var $p=/^[a-z][a-z0-9._-]{0,63}$/;function jp(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(jp,"sanitizeActor");var ir=class{constructor(t,n={}){this.paths=t;this.filePath=(0,Mi.join)(t.logs,"audit.jsonl"),this.now=n.now??(()=>Date.now()),this.maxAppendBytes=n.maxAppendBytes??mo}static{a(this,"AuditLog")}filePath;now;maxAppendBytes;seq=0;append(t,n,r){let o=$p.test(t)?t:"unknown";this.seq+=1;let s={id:`log-${this.now()}-${this.seq}`,at:new Date(this.now()).toISOString(),action:o,actor:jp(n)};r!==void 0&&(s.details=_o(r));let i=tt(s,this.maxAppendBytes,{kind:"audit"});return ho(this.paths.root,this.filePath,i),s}list(t=100){if(!(0,ar.existsSync)(this.filePath))return[];let n=(0,ar.readFileSync)(this.filePath,"utf8").split(`
|
|
5471
|
-
`).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()}};var dr=require("fs"),on=require("path");$();function $i(e){let t=(0,on.join)(e,".ai-access");return{root:t,proposals:(0,on.join)(t,"proposals"),logs:(0,on.join)(t,"logs"),settings:(0,on.join)(t,"settings.json")}}a($i,"resolveAiAccessPaths");function lo(e){(0,dr.mkdirSync)(e,{recursive:!0,mode:pr});try{(0,dr.chmodSync)(e,pr)}catch{}}a(lo,"mkdirSecure");function ji(e){lo(e.root),lo(e.proposals),lo(e.logs)}a(ji,"ensureAiAccessDirs");dt();un();Be();de();dt();Ce();ce();var Hp="Flow revision conflict: the expectedRev is stale. Ask the AI to re-read flows and resubmit a new proposal.";function Hi(e){let t=V(e),n=_n(e)||void 0;return{proposalId:e.id,title:t.title,displayName:t.displayName,flowId:e.payload.flowId,...n?{flowLabel:n}:{}}}a(Hi,"auditProposalMeta");var lr=class{constructor(t,n,r,o){this.proposals=t;this.audit=n;this.adapter=r;this.getDeploymentType=o}static{a(this,"ProposalService")}async approve(t,n,r={}){let o=this.proposals.beginApprove(t),s=Hi(o),i=r.deploymentType??this.getDeploymentType();try{let d=o.type;if(d!=="replace_flow"){let l=this.proposals.markFailed(t,n,`Unsupported proposal type '${d}'`);return this.audit.append("proposal.failed",n,{...s,reason:l.reason}),l}try{let l=await this.adapter.updateFlow({flowId:o.payload.flowId,flow:o.payload.flow,expectedRev:o.expectedRev,deploymentType:i}),c=this.proposals.markApproved(t,n,l.rev);return this.audit.append("proposal.approved",n,{...s,resultRev:l.rev,flowId:o.payload.flowId}),es(this.proposals,o.payload.flowId,t,l.rev),c}catch(l){if(l instanceof F){let u=this.proposals.markRejected(t,n,Hp);return this.audit.append("proposal.rejected",n,{...s,reason:u.reason,code:l.code,expectedRev:l.expectedRev,currentRev:l.currentRev}),u}let c=l instanceof Error?l.message:String(l),p=this.proposals.markFailed(t,n,c);return this.audit.append("proposal.failed",n,{...s,reason:c}),p}}finally{this.proposals.releaseLock(t)}}reject(t,n,r){let o=this.proposals.get(t);if(o.status!=="pending")throw new G(t,o.status);let s=Hi(o),i=this.proposals.markRejected(t,n,r.trim()||"Rejected by admin");return this.audit.append("proposal.rejected",n,{...s,reason:i.reason}),i}};Ae();function Bp(e){let t=a(()=>Promise.reject(new he(e)),"fail");return{kind:"http",getFlows:t,getFlow:t,updateFlow:t}}a(Bp,"misconfiguredAdapter");function Bi(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(Bi,"localAdminBaseUrl");function cr(e,t={}){let n=$i(e.settings.userDir);ji(n);let r=t.settingsStore??new nt(n,e.settings.aiAccess);t.bindSettings!==!1&&fn(r);let o=t.linkStore??rr(e);o.configureDefaults({defaultTtlSeconds:r.getEffective().defaultLinkTtlSeconds,maxActiveLinks:r.getEffective().maxActiveLinks});let s=new bn(n),i=new ir(n),d;if(t.flowAdapter)d=t.flowAdapter;else try{d=Oi({RED:e,http:{baseUrl:Bi(e)},adminAuthEnabled:Y(e.settings)})}catch(p){let u=p instanceof ae?p.message:hn;e.log?.warn?.(`AI Access flow adapter unavailable: ${u}`),d=Bp(u)}let l;if(t.nodeTypesAdapter)l=t.nodeTypesAdapter;else if(t.flowAdapter)l=xr();else try{l=Ho({baseUrl:Bi(e)})}catch(p){let u=p instanceof Error?p.message:String(p);e.log?.warn?.(`AI Access node-types adapter unavailable: ${u}`),l=xr([])}let c=new lr(s,i,d,()=>se(e.settings).defaultDeploymentType);return{linkStore:o,proposalStore:s,auditLog:i,flowAdapter:d,nodeTypesAdapter:l,proposalService:c,settingsStore:r,...t.catalogueFetcher?{catalogueFetcher:t.catalogueFetcher}:{},...t.catalogueUrl?{catalogueUrl:t.catalogueUrl}:{}}}a(cr,"createPluginServices");$();Se();Ae();function qp(e){let t=z(e.settings.httpAdminRoot);return t==="/"?"/mcp":`${t}/mcp`}a(qp,"mcpPageUrl");function qi(e,t,n){let r=t.getEffective(),o=rt(n);return{ok:!0,adminAuthEnabled:Y(e.settings),canEdit:o==="*",permission:o,mcpPageUrl:qp(e),apiBase:me(e.settings.httpAdminRoot),settings:{enabled:r.enabled,allowWithoutAdminAuth:r.allowWithoutAdminAuth,defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,defaultDeploymentType:r.defaultDeploymentType,maxActiveLinks:r.maxActiveLinks},sources:r.sources,disk:t.getDisk(),precedence:["disk","settings.js","default"]}}a(qi,"settingsResponse");function Ui(e,t){return(n,r)=>{r.json(qi(e,t,n))}}a(Ui,"getSettingsHandler");function Gi(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,d={};if(i.enabled!==void 0){if(typeof i.enabled!="boolean"){o.status(400).json({error:"enabled must be a boolean",code:"invalid_body"});return}d.enabled=i.enabled}if(i.allowWithoutAdminAuth!==void 0){if(typeof i.allowWithoutAdminAuth!="boolean"){o.status(400).json({error:"allowWithoutAdminAuth must be a boolean",code:"invalid_body"});return}d.allowWithoutAdminAuth=i.allowWithoutAdminAuth}if(i.defaultLinkTtlSeconds!==void 0){if(typeof i.defaultLinkTtlSeconds!="number"||!Number.isFinite(i.defaultLinkTtlSeconds)||i.defaultLinkTtlSeconds<=0){o.status(400).json({error:"defaultLinkTtlSeconds must be a positive number",code:"invalid_body"});return}d.defaultLinkTtlSeconds=Math.floor(i.defaultLinkTtlSeconds)}if(i.defaultDeploymentType!==void 0){let l=i.defaultDeploymentType;if(l!=="nodes"&&l!=="flows"&&l!=="full"){o.status(400).json({error:"defaultDeploymentType must be 'nodes', 'flows', or 'full'",code:"invalid_body"});return}d.defaultDeploymentType=l}if(i.maxActiveLinks!==void 0){if(typeof i.maxActiveLinks!="number"||!Number.isFinite(i.maxActiveLinks)||i.maxActiveLinks<=0){o.status(400).json({error:"maxActiveLinks must be a positive number",code:"invalid_body"});return}d.maxActiveLinks=Math.floor(i.maxActiveLinks)}try{let l=t.patch(d);n.configureDefaults({defaultTtlSeconds:l.defaultLinkTtlSeconds,maxActiveLinks:l.maxActiveLinks}),o.json(qi(e,t,r))}catch(l){if(l instanceof M){o.status(l.statusCode).json({error:l.message,code:l.code});return}_(o,l);return}}}a(Gi,"putSettingsHandler");Me();Ae();D();un();var Up="com55-ai-access";function Gp(e,t,n){e.httpAdmin.get("/mcp",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.type("html").status(200).send(Si({apiBase:me(e.settings.httpAdminRoot),mcpRpc:Pe(e.settings.httpAdminRoot),editorRoot:z(e.settings.httpAdminRoot),authTokenKey:To(e.settings.httpAdminRoot)}))}),e.httpAdmin.get("/mcp/api/v1",t,n,zr(e)),e.httpAdmin.get("/mcp/api/v1/",t,n,zr(e)),e.httpAdmin.get("/mcp/api/v1/openapi.yaml",t,n,bi()),e.httpAdmin.get("/mcp/api/v1/openapi.json",t,n,vi()),e.httpAdmin.get("/mcp/editor/ai-access-sidebar.js",t,(r,o)=>{o.setHeader("Cache-Control","no-store, no-cache, must-revalidate, max-age=0"),o.setHeader("Pragma","no-cache"),o.setHeader("Expires","0"),o.setHeader("X-AI-Access-Sidebar-Version",xt),o.type("application/javascript").status(200).send(Fn())})}a(Gp,"registerMcpShellAndDiscovery");var Wp=/^\/mcp\/(?:api\/v1\/proposals|rpc$|a\/[^/]+\/(?:proposals|propose|draft))/;function Wi(e,t={}){let n=jn(e.settings.aiAccess??null),{ip:r,token:o}=n,s=cr(e,{...t,linkStore:t.linkStore??rr(e)});e.httpAdmin.use((m,h,y)=>{if(!Wp.test(m.path)){y();return}s.flowAdapter.getFlows().then(x=>Vo(m,Tn(x.flows))).catch(()=>{}).finally(()=>y())}),Zs(e,{linkStore:s.linkStore,proposalStore:s.proposalStore,auditLog:s.auditLog,flowAdapter:s.flowAdapter,nodeTypesAdapter:s.nodeTypesAdapter,...s.catalogueFetcher?{catalogueFetcher:s.catalogueFetcher}:{},...s.catalogueUrl?{catalogueUrl:s.catalogueUrl}:{},rateLimiters:n}),Gp(e,r,o),e.httpAdmin.get("/mcp/api/v1/status",r,o,So(e,m=>s.linkStore.validateResult(m)),v((m,h)=>{h.json(Lt(e,m))})),e.httpAdmin.get("/mcp/api/v1/settings",r,o,Ot(e,"api"),v(Ui(e,s.settingsStore))),e.httpAdmin.put("/mcp/api/v1/settings",r,o,Ot(e,"api"),mr(e),v(Gi(e,s.settingsStore,s.linkStore)));let i=[r,o,Ot(e,"api"),mn(e),mr(e)];e.httpAdmin.get("/mcp/api/v1/links",...i,v(di(e,s.linkStore))),e.httpAdmin.post("/mcp/api/v1/links",...i,v(li(e,s.linkStore,s.auditLog))),e.httpAdmin.delete("/mcp/api/v1/links/:id",...i,v(ci(e,s.linkStore,s.auditLog)));let d=[r,o,mn(e),Ao(e,m=>s.linkStore.validateResult(m))],l=s.flowAdapter,p={nodeTypes:s.nodeTypesAdapter,...s.catalogueFetcher?{catalogueFetcher:s.catalogueFetcher}:{},...s.catalogueUrl?{catalogueUrl:s.catalogueUrl}:{}};e.httpAdmin.get("/mcp/api/v1/flows",...d,v(ws(l))),e.httpAdmin.get("/mcp/api/v1/flows/:id",...d,v(zn(l))),e.httpAdmin.get("/mcp/api/v1/nodes/:id",...d,v(Jn(l))),e.httpAdmin.get("/mcp/api/v1/nodes/:id/field/:name",...d,v(Kn(l))),e.httpAdmin.get("/mcp/api/v1/search",...d,v(Vn(l))),e.httpAdmin.get("/mcp/api/v1/node-types",...d,v(Xn(p))),e.httpAdmin.get("/mcp/api/v1/node-types/:type",...d,v(Yn(p))),e.httpAdmin.get("/mcp/api/v1/palette/search",...d,v(Qn(p)));let u=e.settings.httpAdminRoot;e.httpAdmin.get("/mcp/api/v1/proposals",...d,v(Pi(s.proposalStore,s.flowAdapter,u))),e.httpAdmin.get("/mcp/api/v1/proposals/:id",...d,v(_i(s.proposalStore,u,s.flowAdapter))),e.httpAdmin.get("/mcp/api/v1/proposals/:id/diff",...d,v(Ci(s.proposalStore,s.flowAdapter))),e.httpAdmin.post("/mcp/api/v1/proposals",r,o,kt("rest"),st(e,s.linkStore,"rest"),v(Ti(s.proposalStore,s.auditLog,s.flowAdapter,u,s.nodeTypesAdapter))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/approve",...i,kt("rest"),v(Ri(s.proposalService,u))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/reject",...i,kt("rest"),v(Ei(s.proposalService,u))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/withdraw",r,o,kt("rest"),st(e,s.linkStore,"rest"),v(Li(s.proposalStore,s.auditLog,u)));let f=[r,o,st(e,s.linkStore,"mcp")];e.httpAdmin.post("/mcp/rpc",...f,kt("mcp"),v(gi(e,s))),e.httpAdmin.get("/mcp/rpc",...f,so),e.httpAdmin.delete("/mcp/rpc",...f,so),e.httpAdmin.get("/mcp/api/v1/logs",r,o,Ot(e,"api"),mn(e),v(pi(s.auditLog,s.linkStore))),e.httpAdmin.use("/mcp/api/v1",gs()),oi(e)}a(Wi,"registerRoutes");function zp(e){e.plugins.registerPlugin(Up,{type:"com55-ai-access",onadd(){Wi(e),e.log?.info?.("Node-RED AI Access plugin loaded (/mcp)")}})}a(zp,"registerAiAccessPlugin");var Jp=zp;0&&(module.exports={ACCESS_LINK_INVALID_MESSAGE,ACCESS_LINK_MISSING_MESSAGE,AccessLinkStore,EDITOR_SIDEBAR_ASSET_VERSION,MAX_PROPOSAL_JSON_BYTES,SettingsStore,areAiFeaturesEnabled,bindSettingsStore,createPluginServices,getStorageDir,isAdminAuthEnabled,packageVersion,registerRoutes,renderEditorSidebarJs,renderEditorSidebarLoaderHtml,requireAccessLink,resolveAiAccessSettings});
|
|
5486
|
+
</html>`}a(Di,"renderMcpPage");ce();de();dt();$();$e();O();be();Ne();ce();function Ze(e,t,n,r={}){e.setHeader("X-Content-Type-Options","nosniff"),e.setHeader("Content-Type","application/json; charset=utf-8");let o={error:ve[n]??"Request failed",code:n,...r},s=X(n,"rest");s&&typeof o.hint!="string"&&(o.hint=s),e.status(t).json(o)}a(Ze,"sendSafeJsonError");function Mi(e){let t={kind:"admin"};return typeof e.user?.username=="string"&&(t.username=e.user.username),t}a(Mi,"adminActor");function $i(e,t,n,r,o){return async(s,i)=>{if(!s.accessLink){i.setHeader("WWW-Authenticate",j),i.status(401).json({error:Q,code:ge});return}try{let d=N(s,r),l=K(s.query),c=await Ie(e,t,s.accessLink,s.body??{},n,d,o),p=D(e,c.expectedRev,{flowId:c.payload.flowId}),u=ye(e,e.get(c.id),s.accessLink.id),f=c.warnings;i.status(201).json({...Z(c,{...d,include:l}),mine:!0,...we(u),...H(p),...f&&f.length>0?{warnings:f}:{}})}catch(d){let l=_(d,"rest");if(l){i.status(l.statusCode).json(l.body);return}P(i,d);return}}}a($i,"createProposalHandler");function ji(e,t,n){return async(r,o)=>{let s,i;try{let k=await t.getFlows();s=k.rev,i=xn(k.flows)}catch{}let d={...N(r,n),...i?{liveFlowLabels:i}:{}},l=K(r.query),c;try{c=Ln(r.query)}catch(k){P(o,k);return}let p=s?D(e,s):0,u=r.accessLink;if(u){let k=ht(e,u,d,c),re=k.proposals.map(U=>{let L=ee(e,u,U,{...d,include:l},{includeConflicts:!1});return L.status!=="pending"||s===void 0?L:{...L,baseRevStale:L.expectedRev!==s}});o.json({proposals:re,...Ue({...k,returned:k.proposals.length}),...H(p)});return}let f={};c.flowId&&(f.flowId=c.flowId);let m=os(e.list(f).filter(k=>Br(k.status,c.status)),{...d,include:l}),h=c.offset??0,x=(c.limit!==void 0?m.slice(h,h+c.limit):h>0?m.slice(h):m).map(k=>k.status!=="pending"||s===void 0?k:{...k,baseRevStale:k.expectedRev!==s});o.json({proposals:x,...Ue({total:m.length,limit:c.limit??null,offset:h,returned:x.length}),...H(p)})}}a(ji,"listProposalsHandler");function Hi(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=N(r,t),d=K(r.query),l;if(n)try{l=(await n.getFlows()).rev}catch{}try{if(r.accessLink){let c=_e(e,r.accessLink,s,i);o.json(ee(e,r.accessLink,c,{...i,include:d},{...l?{liveRev:l}:{}}));return}l&&D(e,l),o.json(Z(e.get(s),{...i,include:d}))}catch(c){if(c instanceof J){Ze(o,404,c.code);return}let p=_(c,"rest");if(p){o.status(p.statusCode).json(p.body);return}P(o,c);return}}}a(Hi,"getProposalHandler");function Bi(e,t){return async(n,r)=>{let o=n.params.id;if(typeof o!="string"||o.length===0){r.status(400).json({error:"Missing proposal id"});return}let s=n.body??{},i={};if(s.deploymentType!==void 0){if(s.deploymentType!=="nodes"&&s.deploymentType!=="flows"&&s.deploymentType!=="full"){r.status(400).json({error:"deploymentType must be 'nodes', 'flows', or 'full'"});return}i.deploymentType=s.deploymentType}try{let d=await e.approve(o,Mi(n),i);r.json(V(d,N(n,t)))}catch(d){if(d instanceof J||d instanceof G||d instanceof qt||d instanceof M){Ze(r,d.statusCode,d.code);return}P(r,d);return}}}a(Bi,"approveProposalHandler");function qi(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=V(e.reject(o,Mi(n),i),N(n,t));r.json(d)}catch(d){if(d instanceof J||d instanceof G||d instanceof M){Ze(r,d.statusCode,d.code);return}P(r,d);return}}}a(qi,"rejectProposalHandler");function Ui(e,t,n){return(r,o)=>{if(!r.accessLink){o.setHeader("WWW-Authenticate",j),o.status(401).json({error:Q,code:ge});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=N(r,n),l=K(r.query);try{let c=yt(e,t,r.accessLink,s,i.reason,d);o.json(ee(e,r.accessLink,c,{...d,include:l}))}catch(c){let p=_(c,"rest");if(p){Ze(o,p.statusCode,p.body.code);return}Ze(o,500,"internal_error");return}}}a(Ui,"withdrawProposalHandler");function Gi(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?_e(e,n.accessLink,o):e.get(o),i=Tt(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=_t(await Lt(s,t),i.format);r.json(d)}catch(s){if(s instanceof J){Ze(r,404,s.code);return}if(s instanceof I||s instanceof ae||s instanceof he){Ze(r,s.statusCode,s.code);return}let i=_(s,"rest");if(i){r.status(i.statusCode).json(i.body);return}P(r,s);return}}}a(Gi,"getProposalDiffHandler");de();de();vn();function lu(e){return e.endsWith("/")?e.slice(0,-1):e}a(lu,"trimTrailingSlash");var or=class{static{a(this,"HttpAdapter")}kind="http";baseUrl;accessToken;fetchImpl;constructor(t){this.baseUrl=lu(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 I(n):t.status===409?new F("unknown","unknown",`Admin API ${n}`):t.status===401||t.status===403?new he(gn):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 Ut(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 I(t);let s=await this.readJson(r,"GET /flows"),i=await this.readJson(o,`GET /flow/${t}`);return{rev:Ut(s,"GET /flows").rev,flow:lt(t,i)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new F(t.expectedRev,r.rev,"updateFlow");let o=bn(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 F(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}}};de();vn();function cu(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(cu,"requireFlowsApi");function pu(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(pu,"resolveRev");var sr=class{static{a(this,"RedAdapter")}kind="red";flows;constructor(t){this.flows=cu(t)}async getFlows(){let t=await this.flows.getFlows({});return Ut(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 uu(o)?new I(t):o}return{rev:n.rev,flow:lt(t,r)}}async updateFlow(t){let n=t.deploymentType??"nodes",r=await this.getFlows();if(r.rev!==t.expectedRev)throw new F(t.expectedRev,r.rev,"updateFlow");let o=bn(r.flows,t.flowId,t.flow),s=await this.flows.setFlows({flows:{flows:o,rev:t.expectedRev},deploymentType:n});return{flowId:t.flowId,rev:pu(s)}}};function Wi(e){let t=e?.runtime?.flows;return!!(t?.getFlows&&t.getFlow&&t.setFlows)}a(Wi,"canUseRedRuntime");function uu(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(uu,"isRuntimeFlowNotFound");var zi=require("module");function Ji(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(Ji,"isFlowsApi");function fu(){if(typeof require=="function")return require;let e=(0,eval)("import.meta.url");return(0,zi.createRequire)(e)}a(fu,"createPluginRequire");function mu(e){try{let t=e("@node-red/runtime/lib/settings");return typeof t.available=="function"&&t.available()===!0}catch{return!1}}a(mu,"isNodeRedRuntimeInitialized");function gu(){try{let e=fu();if(!mu(e))return null;let t=e("@node-red/runtime");return Ji(t.flows)?t.flows:null}catch{return null}}a(gu,"tryRequireInitializedRuntimeFlows");function Ki(e){if(e?.runtime&&Ji(e.runtime.flows))return e;let t=gu();return t?{runtime:{flows:t}}:null}a(Ki,"resolveRuntimeHost");function Vi(e={}){let t=Ki(e.RED??null);if(t&&Wi(t))return new sr(t);let n=e.adminAuthEnabled===!0;if(e.http?.baseUrl){if(n&&!e.http.accessToken)throw new ae("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 or(e.http)}throw new ae("No usable Node-RED flow adapter: in-process flows API is unavailable and no HTTP Admin API baseUrl was provided")}a(Vi,"createFlowAdapter");de();var ar=require("fs"),Xi=require("path");yn();$();var hu=/^[a-z][a-z0-9._-]{0,63}$/;function yu(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(yu,"sanitizeActor");var ir=class{constructor(t,n={}){this.paths=t;this.filePath=(0,Xi.join)(t.logs,"audit.jsonl"),this.now=n.now??(()=>Date.now()),this.maxAppendBytes=n.maxAppendBytes??ho}static{a(this,"AuditLog")}filePath;now;maxAppendBytes;seq=0;append(t,n,r){let o=hu.test(t)?t:"unknown";this.seq+=1;let s={id:`log-${this.now()}-${this.seq}`,at:new Date(this.now()).toISOString(),action:o,actor:yu(n)};r!==void 0&&(s.details=Fo(r));let i=tt(s,this.maxAppendBytes,{kind:"audit"});return wo(this.paths.root,this.filePath,i),s}list(t=100){if(!(0,ar.existsSync)(this.filePath))return[];let n=(0,ar.readFileSync)(this.filePath,"utf8").split(`
|
|
5487
|
+
`).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()}};var dr=require("fs"),rn=require("path");$();function Yi(e){let t=(0,rn.join)(e,".ai-access");return{root:t,proposals:(0,rn.join)(t,"proposals"),logs:(0,rn.join)(t,"logs"),settings:(0,rn.join)(t,"settings.json")}}a(Yi,"resolveAiAccessPaths");function po(e){(0,dr.mkdirSync)(e,{recursive:!0,mode:pr});try{(0,dr.chmodSync)(e,pr)}catch{}}a(po,"mkdirSecure");function Qi(e){po(e.root),po(e.proposals),po(e.logs)}a(Qi,"ensureAiAccessDirs");dt();pn();xe();Be();de();dt();Ne();ce();var wu="Flow revision conflict: the expectedRev is stale. Ask the AI to re-read flows and resubmit a new proposal.";function Zi(e){let t=V(e),n=_n(e)||void 0;return{proposalId:e.id,title:t.title,displayName:t.displayName,flowId:e.payload.flowId,...n?{flowLabel:n}:{}}}a(Zi,"auditProposalMeta");var lr=class{constructor(t,n,r,o){this.proposals=t;this.audit=n;this.adapter=r;this.getDeploymentType=o}static{a(this,"ProposalService")}async approve(t,n,r={}){let o=this.proposals.beginApprove(t),s=Zi(o),i=r.deploymentType??this.getDeploymentType();try{let d=o.type;if(d!=="replace_flow"){let l=this.proposals.markFailed(t,n,`Unsupported proposal type '${d}'`);return this.audit.append("proposal.failed",n,{...s,reason:l.reason}),l}try{let l=await this.adapter.updateFlow({flowId:o.payload.flowId,flow:o.payload.flow,expectedRev:o.expectedRev,deploymentType:i}),c=this.proposals.markApproved(t,n,l.rev);return this.audit.append("proposal.approved",n,{...s,resultRev:l.rev,flowId:o.payload.flowId}),as(this.proposals,o.payload.flowId,t,l.rev),c}catch(l){if(l instanceof F){let u=this.proposals.markRejected(t,n,wu);return this.audit.append("proposal.rejected",n,{...s,reason:u.reason,code:l.code,expectedRev:l.expectedRev,currentRev:l.currentRev}),u}let c=l instanceof Error?l.message:String(l),p=this.proposals.markFailed(t,n,c);return this.audit.append("proposal.failed",n,{...s,reason:c}),p}}finally{this.proposals.releaseLock(t)}}reject(t,n,r){let o=this.proposals.get(t);if(o.status!=="pending")throw new G(t,o.status);let s=Zi(o),i=this.proposals.markRejected(t,n,r.trim()||"Rejected by admin");return this.audit.append("proposal.rejected",n,{...s,reason:i.reason}),i}};Ae();function bu(e){let t=a(()=>Promise.reject(new he(e)),"fail");return{kind:"http",getFlows:t,getFlow:t,updateFlow:t}}a(bu,"misconfiguredAdapter");function ea(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(ea,"localAdminBaseUrl");function cr(e,t={}){let n=Yi(e.settings.userDir);Qi(n);let r=t.settingsStore??new nt(n,e.settings.aiAccess);t.bindSettings!==!1&&un(r),Lo(e.settings.aiAccess?.publicBaseUrl);let o=t.linkStore??rr(e);o.configureDefaults({defaultTtlSeconds:r.getEffective().defaultLinkTtlSeconds,maxActiveLinks:r.getEffective().maxActiveLinks});let s=new wn(n),i=new ir(n),d;if(t.flowAdapter)d=t.flowAdapter;else try{d=Vi({RED:e,http:{baseUrl:ea(e)},adminAuthEnabled:Y(e.settings)})}catch(p){let u=p instanceof ae?p.message:gn;e.log?.warn?.(`AI Access flow adapter unavailable: ${u}`),d=bu(u)}let l;if(t.nodeTypesAdapter)l=t.nodeTypesAdapter;else if(t.flowAdapter)l=_r();else try{l=Jo({baseUrl:ea(e)})}catch(p){let u=p instanceof Error?p.message:String(p);e.log?.warn?.(`AI Access node-types adapter unavailable: ${u}`),l=_r([])}let c=new lr(s,i,d,()=>se(e.settings).defaultDeploymentType);return{linkStore:o,proposalStore:s,auditLog:i,flowAdapter:d,nodeTypesAdapter:l,proposalService:c,settingsStore:r,...t.catalogueFetcher?{catalogueFetcher:t.catalogueFetcher}:{},...t.catalogueUrl?{catalogueUrl:t.catalogueUrl}:{}}}a(cr,"createPluginServices");$();Se();Ae();function vu(e){let t=z(e.settings.httpAdminRoot);return t==="/"?"/mcp":`${t}/mcp`}a(vu,"mcpPageUrl");function ta(e,t,n){let r=t.getEffective(),o=rt(n);return{ok:!0,adminAuthEnabled:Y(e.settings),canEdit:o==="*",permission:o,mcpPageUrl:vu(e),apiBase:me(e.settings.httpAdminRoot),settings:{enabled:r.enabled,allowWithoutAdminAuth:r.allowWithoutAdminAuth,defaultLinkTtlSeconds:r.defaultLinkTtlSeconds,defaultDeploymentType:r.defaultDeploymentType,maxActiveLinks:r.maxActiveLinks},sources:r.sources,disk:t.getDisk(),precedence:["disk","settings.js","default"]}}a(ta,"settingsResponse");function na(e,t){return(n,r)=>{r.json(ta(e,t,n))}}a(na,"getSettingsHandler");function ra(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,d={};if(i.enabled!==void 0){if(typeof i.enabled!="boolean"){o.status(400).json({error:"enabled must be a boolean",code:"invalid_body"});return}d.enabled=i.enabled}if(i.allowWithoutAdminAuth!==void 0){if(typeof i.allowWithoutAdminAuth!="boolean"){o.status(400).json({error:"allowWithoutAdminAuth must be a boolean",code:"invalid_body"});return}d.allowWithoutAdminAuth=i.allowWithoutAdminAuth}if(i.defaultLinkTtlSeconds!==void 0){if(typeof i.defaultLinkTtlSeconds!="number"||!Number.isFinite(i.defaultLinkTtlSeconds)||i.defaultLinkTtlSeconds<=0){o.status(400).json({error:"defaultLinkTtlSeconds must be a positive number",code:"invalid_body"});return}d.defaultLinkTtlSeconds=Math.floor(i.defaultLinkTtlSeconds)}if(i.defaultDeploymentType!==void 0){let l=i.defaultDeploymentType;if(l!=="nodes"&&l!=="flows"&&l!=="full"){o.status(400).json({error:"defaultDeploymentType must be 'nodes', 'flows', or 'full'",code:"invalid_body"});return}d.defaultDeploymentType=l}if(i.maxActiveLinks!==void 0){if(typeof i.maxActiveLinks!="number"||!Number.isFinite(i.maxActiveLinks)||i.maxActiveLinks<=0){o.status(400).json({error:"maxActiveLinks must be a positive number",code:"invalid_body"});return}d.maxActiveLinks=Math.floor(i.maxActiveLinks)}try{let l=t.patch(d);n.configureDefaults({defaultTtlSeconds:l.defaultLinkTtlSeconds,maxActiveLinks:l.maxActiveLinks}),o.json(ta(e,t,r))}catch(l){if(l instanceof M){o.status(l.statusCode).json({error:l.message,code:l.code});return}P(o,l);return}}}a(ra,"putSettingsHandler");$e();Ae();O();pn();var ku="com55-ai-access";function Au(e,t,n){e.httpAdmin.get("/mcp",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.type("html").status(200).send(Di({apiBase:me(e.settings.httpAdminRoot),mcpRpc:Pe(e.settings.httpAdminRoot),editorRoot:z(e.settings.httpAdminRoot),authTokenKey:No(e.settings.httpAdminRoot)}))}),e.httpAdmin.get("/mcp/api/v1",t,n,Kr(e)),e.httpAdmin.get("/mcp/api/v1/",t,n,Kr(e)),e.httpAdmin.get("/mcp/api/v1/openapi.yaml",t,n,Ni()),e.httpAdmin.get("/mcp/api/v1/openapi.json",t,n,Ii()),e.httpAdmin.get("/mcp/editor/ai-access-sidebar.js",t,(r,o)=>{o.setHeader("Cache-Control","no-store, no-cache, must-revalidate, max-age=0"),o.setHeader("Pragma","no-cache"),o.setHeader("Expires","0"),o.setHeader("X-AI-Access-Sidebar-Version",xt),o.type("application/javascript").status(200).send(In())})}a(Au,"registerMcpShellAndDiscovery");var Su=/^\/mcp\/(?:api\/v1\/proposals|rpc$|a\/[^/]+\/(?:proposals|propose|draft))/;function oa(e,t={}){let n=$n(e.settings.aiAccess??null),{ip:r,token:o}=n,s=cr(e,{...t,linkStore:t.linkStore??rr(e)}),i=xs(e.settings.aiAccess?.corsAllowedOrigins);_s(e.httpAdmin,Ts(()=>i)),e.httpAdmin.use((h,y,x)=>{if(!Su.test(h.path)){x();return}s.flowAdapter.getFlows().then(k=>ns(h,xn(k.flows))).catch(()=>{}).finally(()=>x())}),ui(e,{linkStore:s.linkStore,proposalStore:s.proposalStore,auditLog:s.auditLog,flowAdapter:s.flowAdapter,nodeTypesAdapter:s.nodeTypesAdapter,...s.catalogueFetcher?{catalogueFetcher:s.catalogueFetcher}:{},...s.catalogueUrl?{catalogueUrl:s.catalogueUrl}:{},rateLimiters:n}),Au(e,r,o),e.httpAdmin.get("/mcp/api/v1/status",r,o,To(e,h=>s.linkStore.validateResult(h)),v((h,y)=>{y.json(Ct(e,h))})),e.httpAdmin.get("/mcp/api/v1/settings",r,o,Mt(e,"api"),v(na(e,s.settingsStore))),e.httpAdmin.put("/mcp/api/v1/settings",r,o,Mt(e,"api"),mr(e),v(ra(e,s.settingsStore,s.linkStore)));let d=[r,o,Mt(e,"api"),fn(e),mr(e)];e.httpAdmin.get("/mcp/api/v1/links",...d,v(ki(e,s.linkStore))),e.httpAdmin.post("/mcp/api/v1/links",...d,v(Ai(e,s.linkStore,s.auditLog))),e.httpAdmin.delete("/mcp/api/v1/links/:id",...d,v(Si(e,s.linkStore,s.auditLog)));let l=[r,o,fn(e),xo(e,h=>s.linkStore.validateResult(h))],c=s.flowAdapter,u={nodeTypes:s.nodeTypesAdapter,...s.catalogueFetcher?{catalogueFetcher:s.catalogueFetcher}:{},...s.catalogueUrl?{catalogueUrl:s.catalogueUrl}:{}};e.httpAdmin.get("/mcp/api/v1/flows",...l,v(Ls(c))),e.httpAdmin.get("/mcp/api/v1/flows/:id",...l,v(Wn(c))),e.httpAdmin.get("/mcp/api/v1/nodes/:id",...l,v(zn(c))),e.httpAdmin.get("/mcp/api/v1/nodes/:id/field/:name",...l,v(Jn(c))),e.httpAdmin.get("/mcp/api/v1/search",...l,v(Kn(c))),e.httpAdmin.get("/mcp/api/v1/node-types",...l,v(Vn(u))),e.httpAdmin.get("/mcp/api/v1/node-types/:type",...l,v(Xn(u))),e.httpAdmin.get("/mcp/api/v1/palette/search",...l,v(Yn(u)));let f=e.settings.httpAdminRoot;e.httpAdmin.get("/mcp/api/v1/proposals",...l,v(ji(s.proposalStore,s.flowAdapter,f))),e.httpAdmin.get("/mcp/api/v1/proposals/:id",...l,v(Hi(s.proposalStore,f,s.flowAdapter))),e.httpAdmin.get("/mcp/api/v1/proposals/:id/diff",...l,v(Gi(s.proposalStore,s.flowAdapter))),e.httpAdmin.post("/mcp/api/v1/proposals",r,o,kt("rest"),st(e,s.linkStore,"rest"),v($i(s.proposalStore,s.auditLog,s.flowAdapter,f,s.nodeTypesAdapter))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/approve",...d,kt("rest"),v(Bi(s.proposalService,f))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/reject",...d,kt("rest"),v(qi(s.proposalService,f))),e.httpAdmin.post("/mcp/api/v1/proposals/:id/withdraw",r,o,kt("rest"),st(e,s.linkStore,"rest"),v(Ui(s.proposalStore,s.auditLog,f)));let m=[r,o,st(e,s.linkStore,"mcp")];e.httpAdmin.post("/mcp/rpc",...m,kt("mcp"),v(Ri(e,s))),e.httpAdmin.get("/mcp/rpc",...m,ao),e.httpAdmin.delete("/mcp/rpc",...m,ao),e.httpAdmin.get("/mcp/api/v1/logs",r,o,Mt(e,"api"),fn(e),v(xi(s.auditLog,s.linkStore))),e.httpAdmin.use("/mcp/api/v1",Ps()),yi(e)}a(oa,"registerRoutes");function xu(e){e.plugins.registerPlugin(ku,{type:"com55-ai-access",onadd(){oa(e),e.log?.info?.("Node-RED AI Access plugin loaded (/mcp)")}})}a(xu,"registerAiAccessPlugin");var Tu=xu;0&&(module.exports={ACCESS_LINK_INVALID_MESSAGE,ACCESS_LINK_MISSING_MESSAGE,AccessLinkStore,EDITOR_SIDEBAR_ASSET_VERSION,MAX_PROPOSAL_JSON_BYTES,SettingsStore,areAiFeaturesEnabled,bindSettingsStore,createPluginServices,getStorageDir,isAdminAuthEnabled,packageVersion,registerRoutes,renderEditorSidebarJs,renderEditorSidebarLoaderHtml,requireAccessLink,resolveAiAccessSettings});
|