@fugood/buttress-server 2.25.1-beta.3 → 2.25.1-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.mjs +2 -2
- package/lib/types.d.ts +1 -1
- package/lib/utils/SessionFileManager.d.ts +1 -0
- package/package.json +2 -2
package/lib/index.mjs
CHANGED
|
@@ -1561,7 +1561,7 @@ export default async function (
|
|
|
1561
1561
|
|
|
1562
1562
|
return { seconds: Number(String(stdout).trim()) }
|
|
1563
1563
|
}
|
|
1564
|
-
`)]),{seeded:t}},wf=async(e,t=864e5)=>{let n;try{n=await c.readdir(e,{withFileTypes:!0})}catch{return 0}let r=Date.now()-t,i=0;return await Promise.all(n.map(async t=>{if(!t.isDirectory())return;let n=w.join(e,t.name);try{if((await c.stat(n)).mtimeMs>=r)return;await c.rm(n,{recursive:!0,force:!0}),i+=1}catch{}})),i},Tf=e=>e===`~`||e.startsWith(`~/`)?w.join(T.homedir(),e.slice(1)):e,Ef=(e,t)=>{if(typeof e==`number`&&e>0)return e;if(typeof e==`string`){let t=W(e);if(typeof t==`number`&&t>0)return t}return t},Df=(e,{configDir:t=process.cwd(),env:n=process.env}={})=>{let r=e?.functions;if(!(n.ENABLE_FUNCTIONS_ENDPOINT===`1`||r?.enabled===!0))return null;let i=n.BUTTRESS_FUNCTIONS_DIR||r?.dir;if(!i||typeof i!=`string`)return console.warn(`[Functions] disabled: no directory configured. Set [functions] dir = "./functions" in your config (or BUTTRESS_FUNCTIONS_DIR).`),null;let a=Tf(i);return{enabled:!0,dir:w.isAbsolute(a)?a:w.resolve(t,a),allowUnauthenticated:n.BUTTRESS_FUNCTIONS_ALLOW_UNAUTHENTICATED===`1`||r?.allow_unauthenticated===!0,defaultTimeoutMs:Ef(r?.default_timeout,3e5),hotReload:n.BUTTRESS_FUNCTIONS_HOT_RELOAD===`1`||r?.hot_reload===!0,userConfig:r?.config&&typeof r.config==`object`?{...r.config}:{}}},Of=async(e,{tempFileDir:t})=>{console.log(`[Functions] Local functions are EXPERIMENTAL: endpoints, the file contract and the context API may change between releases.`);let{seeded:n}=await Cf(e.dir);n&&console.log(`[Functions] Prepared functions directory: ${e.dir}`);let r=await wf(w.join(t,`functions`));r>0&&console.log(`[Functions] Removed ${r} stale scratch director${r===1?`y`:`ies`}`);let i=Gd(e),a=Yd(e),o={enabled:!0,count:0},s=async()=>{let e=await i.list();return o.count=e.length,e},c=await s();console.log(`[Functions] Loaded ${c.length} function${c.length===1?``:`s`} from ${e.dir}`);try{let e=await a.get();if(e){let t=e.mode===`override`?`replaces workspace auth`:`runs after workspace auth`;console.log(`[Functions] Custom auth: ${w.basename(e.file)} (mode: ${e.mode} — ${t})`)}}catch{}let l=null;return e.hotReload&&(l=yf(e.dir,e=>{let t=[...e].slice(0,3).join(`, `);console.log(`[Functions] Change detected (${t}${e.size>3?`, …`:``}); reloading`),Promise.allSettled([s(),a.get()])}),l&&console.log(`[Functions] Hot reload: watching ${e.dir}`)),{dispose:()=>l?.close(),config:e,dir:e.dir,stats:o,list:s,describe:e=>i.describe(e),getCustomAuth:()=>a.get(),call:async(t,n,{runtime:r,emit:a,signal:o,callId:s,surface:c=`http`})=>{let l=Date.now();try{let u=await _f(await i.get(t),n,{runtime:r,functionsConfig:e,emit:a,signal:o,callId:s});return Hu.recordCall({name:t,surface:c,durationMs:Date.now()-l,success:!0}),u}catch(e){throw Hu.recordCall({name:t,surface:c,durationMs:Date.now()-l,success:!1,error:e?.name&&e.name!==`Error`?e.name:e?.message||String(e)}),e}}}},kf=256*1024,Af=e=>{if(e===void 0)return``;let t=typeof e==`string`?e:JSON.stringify(e,null,2)??String(e);return t.length<=kf?t:`${t.slice(0,kf)}\n… [truncated ${t.length-kf} characters]`},jf=({functions:e,runtime:t,version:n,signal:r})=>{let i=new _e({name:`buttress-functions`,version:n},{capabilities:{tools:{}}});return i.setRequestHandler(ye,async()=>({tools:(await e.list()).map(e=>({name:e.name,description:e.description,inputSchema:e.parameters}))})),i.setRequestHandler(ve,async n=>{let{name:i,arguments:a}=n.params;try{let n=await e.call(i,a??{},{runtime:t,signal:r,surface:`mcp`});return{content:[{type:`text`,text:Af(n)}]}}catch(e){return{content:[{type:`text`,text:`Error: ${e?.message||String(e)}`}],isError:!0}}}),i};var Mf=class{onmessage;onerror;onclose;responses=[];resolveResponse=null;async start(){}async send(e){if(this.resolveResponse){let t=this.resolveResponse;this.resolveResponse=null,t(e);return}this.responses.push(e)}async close(){this.onclose?.()}async exchange(e){let t=e?.id!==void 0&&e?.id!==null;if(t&&typeof e.method!=`string`)return{jsonrpc:`2.0`,id:e.id,error:{code:-32600,message:`Invalid Request`}};let n=t?new Promise(t=>{let n=this.responses.shift();if(n){t(n);return}this.resolveResponse=t,this.onerror=n=>{this.resolveResponse===t&&(this.resolveResponse=null,t({jsonrpc:`2.0`,id:e.id,error:{code:-32603,message:n?.message||`Internal error`}}))}}):Promise.resolve(null);return this.onmessage?.(e),n}};const Nf=async(e,t)=>{let n=jf(e),r=new Mf;try{if(await n.connect(r),Array.isArray(t)){let e=[];for(let n of t){let t=await r.exchange(n);t&&e.push(t)}return e.length>0?e:null}return await r.exchange(t)}finally{await n.close().catch(()=>{})}},Pf=(e,t,n)=>{Object.defineProperty(e,t,{configurable:!0,enumerable:!0,value:n,writable:!0})},Ff=e=>typeof e==`object`&&!!e&&typeof e.stream==`function`&&typeof e.arrayBuffer==`function`&&typeof e.name==`string`,If=async(e,t)=>{await c.mkdir(e,{recursive:!0});let n=ff(t.name);for(let r=0;r<100;r+=1){let i=r===0?n:`${r}-${n}`,a=w.join(e,i);try{await xe(be.fromWeb(t.stream()),N(a,{flags:`wx`}))}catch(e){if(e?.code===`EEXIST`)continue;throw e}return{path:a,name:i,size:(await c.stat(a)).size}}throw Error(`Could not find a free name for upload "${n}"`)};var Lf=class extends Error{constructor(e){super(e),this.name=`MultipartInputError`}};const Rf=async(e,t)=>{let n={},r=e.input;if(Ff(r))throw new Lf(`The "input" field must be a JSON object, not a file`);if(typeof r==`string`&&r.trim()!==``){let e;try{e=JSON.parse(r)}catch{throw new Lf(`The "input" field must be valid JSON`)}if(typeof e!=`object`||!e||Array.isArray(e))throw new Lf(`The "input" field must be a JSON object`);n=e}let i=[];for(let[r,a]of Object.entries(e)){if(r===`input`)continue;let e=(Array.isArray(a)?a:[a]).filter(Ff);if(e.length===0){typeof a==`string`&&Pf(n,r,a);continue}let o=[];for(let n of e){let e=await If(t,n);o.push(e),i.push({...e,field:r})}Pf(n,r,o.length===1?o[0].path:o.map(e=>e.path))}return{input:n,staged:i}};var zf=class extends Error{constructor(e){super(e),this.name=`QueryInputError`}};const Bf=new Set([`stream`,`token`,`access_token`]),Vf=e=>{if(typeof e!=`object`||!e)return;let{type:t}=e;if(typeof t==`string`)return t;if(Array.isArray(t))return t.find(e=>typeof e==`string`&&e!==`null`)},Hf=e=>{try{return JSON.parse(e)}catch{return}},Uf=(e,t)=>{switch(Vf(t)){case`number`:case`integer`:{if(e.trim()===``)return e;let t=Number(e);return Number.isFinite(t)?t:e}case`boolean`:{let t=e.toLowerCase();return t===``||t===`true`||t===`1`?!0:t===`false`||t===`0`?!1:e}case`object`:{let t=Hf(e);return typeof t==`object`&&t&&!Array.isArray(t)?t:e}case`array`:return Wf([e],t);default:return e}},Wf=(e,t)=>{let n=t?.items;if(e.length>1)return e.map(e=>Uf(e,n));let r=e[0];if(r===``)return[];if(r.startsWith(`[`)){let e=Hf(r);if(Array.isArray(e))return e}return r.split(`,`).map(e=>Uf(e,n))},Gf=(e,t)=>{let n={},r=e.get(`input`);if(r!=null&&r.trim()!==``){let e=Hf(r);if(e===void 0)throw new zf(`The "input" parameter must be valid JSON`);if(typeof e!=`object`||!e||Array.isArray(e))throw new zf(`The "input" parameter must be a JSON object`);n=e}let i=t?.properties??{};for(let t of new Set(e.keys())){if(t===`input`||Bf.has(t))continue;let r=e.getAll(t),a=i[t];Pf(n,t,Vf(a)===`array`||r.length>1?Wf(r,a):Uf(r[0],a))}return n},Kf=(e,t)=>({error:{code:e,message:t}}),qf=(e,t)=>{try{JSON.stringify(e)}catch(e){throw new Jf(t,e?.message||String(e))}};var Jf=class extends Error{constructor(e,t){super(`Function "${e}" returned a value that cannot be encoded as JSON: ${t}`),this.name=`UnserializableResultError`}};const Yf=e=>e instanceof Wd?{status:404,body:Kf(`FUNCTION_NOT_FOUND`,e.message)}:e instanceof mf?{status:504,body:Kf(`FUNCTION_TIMEOUT`,e.message)}:e instanceof Jf?{status:500,body:Kf(`FUNCTION_RESULT_NOT_SERIALIZABLE`,e.message)}:{status:500,body:Kf(`FUNCTION_FAILED`,e?.message||String(e))},Xf=(e,t)=>e.stream===`1`||e.stream===`true`||String(t?.accept||``).includes(`text/event-stream`);async function*Zf(e,t,n,r){let a=[],o=null,s=!1,c=()=>r.signal?.aborted===!0,l=(e,t)=>{a.push({event:e,data:t}),o?.()},u=e.call(t,n,{...r,emit:l}).then(e=>(qf(e,t),{ok:!0,value:e})).then(e=>e,e=>({ok:!1,error:e})).then(e=>(s=!0,o?.(),e));for(;;){for(;a.length>0;){let e=a.shift();if(c())return;yield i({event:`progress`,data:JSON.stringify(e)})}if(s)break;await new Promise(e=>{o=e,(a.length>0||s)&&e()}),o=null}let d=await u;if(c())return;if(d.ok){yield i({event:`result`,data:JSON.stringify({result:d.value})});return}let{body:f}=Yf(d.error);yield i({event:`error`,data:JSON.stringify(f)})}function Qf(e,t){let n=Su({prefix:`/functions`});n.use(ce({origin:e.global?.functions?.cors_allowed_origins??!1,methods:[`GET`,`POST`,`OPTIONS`],allowedHeaders:[`Content-Type`,`Authorization`,`Mcp-Session-Id`,`Mcp-Protocol-Version`],maxAge:86400,preflight:!0})),n.onBeforeHandle(Ed({allowUnauthenticated:t.config.allowUnauthenticated,allowedOrigins:e.global?.functions?.cors_allowed_origins,getCustomAuth:t.getCustomAuth}));let i=async({name:e,input:n,query:r,headers:i,store:a,set:o,request:s,callId:l,stagedDir:u})=>{let d={backend:a.backend,config:a.config};if(Xf(r,i))return Zf(t,e,n,{runtime:d,signal:s.signal,callId:l,surface:`sse`});try{let r=await t.call(e,n,{runtime:d,signal:s.signal,callId:l});return qf(r,e),{result:r}}catch(e){e instanceof Wd&&u&&await c.rm(u,{recursive:!0,force:!0}).catch(()=>{});let{status:t,body:n}=Yf(e);return o.status=t,n}};n.get(`/`,async()=>({functions:await t.list()})),n.post(`/mcp`,async({body:e,store:n,set:r,request:i})=>{let a=e;return!a||typeof a!=`object`?(r.status=400,Kf(`INVALID_REQUEST`,`Expected a JSON-RPC message body`)):await Nf({functions:t,runtime:{backend:n.backend,config:n.config},version:n.serverInfo.version,signal:i.signal},a)||(r.status=202,null)});let o=({set:e})=>(e.status=405,Kf(`METHOD_NOT_ALLOWED`,`This MCP endpoint only accepts POST`));return n.get(`/mcp`,o),n.delete(`/mcp`,o),n.get(`/files/*`,async({params:e,store:t,set:n})=>{let i=w.join(t.config.server.temp_file_dir,`functions`),a=e[`*`]??``,o=uf(i,a);if(o)try{let e=await c.stat(o);if(e.isFile())return Hu.recordDownload({path:w.relative(i,o),size:e.size,success:!0}),r(o)}catch{}return Hu.recordDownload({path:a,success:!1}),n.status=404,Kf(`FUNCTION_FILE_NOT_FOUND`,`No such function output file`)}),n.post(`/upload`,async({body:e,store:t,set:n})=>{let r=e.file,i=w.join(t.config.server.temp_file_dir,`functions`),a=w.join(i,`upload-${S()}`);try{let e=await If(a,r);return Hu.recordUpload({source:`upload`,name:e.name,size:e.size,success:!0}),{...e,url:df(i,e.path)}}catch(e){return await c.rm(a,{recursive:!0,force:!0}).catch(()=>{}),Hu.recordUpload({source:`upload`,name:typeof r?.name==`string`?r.name:`-`,size:typeof r?.size==`number`?r.size:0,success:!1,error:e?.message||String(e)}),n.status=500,Kf(`UPLOAD_FAILED`,e?.message||String(e))}},{body:a.Object({file:a.File()})}),n.post(`/:name`,async({params:e,body:t,query:n,headers:r,store:a,set:o,request:s})=>{let{name:l}=e,u=t??{},d,f;if(String(r[`content-type`]||r[`Content-Type`]||``).includes(`multipart/form-data`)){if(!yd.test(l))return o.status=404,Kf(`FUNCTION_NOT_FOUND`,`Unknown function "${l}"`);d=S();let e=w.join(a.config.server.temp_file_dir,`functions`),n=w.join(e,`${l}-${d}`);try{let e=await Rf(t,w.join(n,`uploads`));u=e.input,f=n;for(let t of e.staged)Hu.recordUpload({source:`call`,name:t.name,size:t.size,success:!0})}catch(e){return await c.rm(n,{recursive:!0,force:!0}).catch(()=>{}),Hu.recordUpload({source:`call`,name:`-`,size:0,success:!1,error:e?.message||String(e)}),e instanceof Lf?(o.status=400,Kf(`INVALID_INPUT`,e.message)):(o.status=500,Kf(`UPLOAD_FAILED`,e?.message||String(e)))}}return typeof u!=`object`||Array.isArray(u)?(o.status=400,Kf(`INVALID_INPUT`,`Function input must be a JSON object`)):i({name:l,input:u,query:n,headers:r,store:a,set:o,request:s,callId:d,stagedDir:f})}),n.get(`/:name`,async({params:e,query:n,headers:r,store:a,set:o,request:s})=>{let{name:c}=e,l=await t.describe(c).catch(()=>null),u;try{u=Gf(new URL(s.url).searchParams,l?.parameters)}catch(e){if(!(e instanceof zf))throw e;return o.status=400,Kf(`INVALID_INPUT`,e.message)}return o.headers&&(o.headers[`cache-control`]=`no-store`),i({name:c,input:u,query:n,headers:r,store:a,set:o,request:s})}),n}const $f=(e={},t={})=>{let n=Array.isArray(e)?[...e]:{...e};return Object.entries(t||{}).forEach(([e,t])=>{t&&typeof t==`object`&&!Array.isArray(t)?n[e]=$f(n[e]||{},t):n[e]=t}),n},ep=e=>e&&typeof e==`object`?structuredClone(e):null,tp=(e,t)=>{let n=ep(e)||{},r=ep(t)||{};return $f(n,r)},np=(e,t)=>$f(structuredClone(e.global),t||{}),rp=(e,t,n,r)=>{if(e.generators.length>0){let i=e.generators.filter(e=>e?.type===n);if(i.length>0&&r){let a=i.find(e=>t.getModelIdentifier(n,e)===r);if(a)return np(e,a)}}return Object.keys(e.global).length>0?np(e,{}):null},ip={udp:{port:8089,announcements:{enabled:!0,interval:5e3},requests:{enabled:!0,responseDelay:100}},http:{enabled:!0,path:`/buttress/info`,cors:!0}},ap=e=>e?e===!0?{...ip}:$f(ip,e):null,op=(e,t)=>{if(!e.generators||e.generators.length===0)return t.map(e=>({type:e}));let n=new Set;return e.generators.forEach(e=>{e.type&&n.add(e.type)}),n.size===0?t.map(e=>({type:e})):Array.from(n).map(e=>({type:e}))},sp=(e,t,n)=>e===void 0?n:typeof e==`number`?e:t(e)??n,cp=6e4,lp=1024*1024*50,up=e=>{let t=Se.machineIdSync(),n={server:{id:`buttress-${t}`,name:`Buttress Server (${t.slice(-8)})`,port:2080,temp_file_dir:w.join(T.tmpdir(),`.buttress`),session_timeout:cp,max_body_size:lp},autodiscover:!1},r=$f(n,ep(e)||{}),i=Array.isArray(r.generators)?r.generators:[],{server:a,generators:o,autodiscover:s,...c}=r;return{autodiscover:ap(s),server:{id:a.id,name:a.name,port:a.port,log_level:a.log_level,temp_file_dir:a.temp_file_dir,max_body_size:sp(a.max_body_size,ee.parse,lp),session_timeout:sp(a.session_timeout,e=>W(e),cp)},global:c,generators:i}},dp={getCapabilities:H.tuple([H.object({type:H.string().optional().default(`ggml-llm`),config:H.any().optional(),currentClientCapabilities:H.any().optional(),options:H.any().optional()}).nullable().optional()]),startGenerator:H.tuple([H.string(),H.any().optional()]),finalizeGenerator:H.tuple([H.string()])},fp=(e,t)=>!!(e&&Object.prototype.hasOwnProperty.call(e,t)),pp=e=>{let t=ep(e);if(!t)return null;let n=ep(t.runtime),r=ep(t.model);if(fp(n,`cache_dir`))return`runtime.cache_dir`;if(fp(r,`local_path`))return`model.local_path`;if(fp(r,`mmproj_local_path`))return`model.mmproj_local_path`;if(fp(r,`allow_local_file`))return`model.allow_local_file`;for(let[e,t]of[[`model`,r],[`runtime`,n]])for(let n of[`model_draft`,`draft_model`]){if(!fp(t,n))continue;let r=t?.[n];if(typeof r==`string`&&!/^https?:\/\//i.test(r)||fp(ep(r),`local_path`))return`${e}.${n}.local_path`}return null},mp=(e,t,n,r)=>{let i=pp(r);if(i)throw Error(`Remote generator config cannot override server filesystem path "${i}"`);let a=ep(r),o=tp(rp(e,t,n,t.getModelIdentifier(n,a)),r);if(Object.keys(o).length===0)throw Error(`Buttress server missing generator configuration`);return o.backend=o.backend||{},o.backend.type||(o.backend.type=n),o};var hp={async getCapabilities({backend:e,config:t},n=null){console.log(`[Server] Get Capabilities:`,n);let{type:r=`ggml-llm`,config:i,currentClientCapabilities:a=null,options:o={}}=n||{type:`ggml-llm`},s=mp(t,e,r,i);return e.getCapabilities(r,a,{...o,config:s})},async startGenerator({backend:e,config:t,session:n},r,i){console.log(`[Server] Start Generator:`,r,i);let a=mp(t,e,r,i),o=await e.startGenerator(r,a);return n.generators.add(o.id),o},async finalizeGenerator({backend:e,session:t},n){return console.log(`[Server] Finalize Generator:`,n),t.generators.delete(n),e.finalizeGenerator(n)}};const gp={initContext:H.tuple([H.string(),H.any().optional()]),completion:H.tuple([H.string(),H.any().optional()]),tokenize:H.tuple([H.string(),H.any()]),detokenize:H.tuple([H.string(),H.any()]),applyChatTemplate:H.tuple([H.string(),H.any()]),releaseContext:H.tuple([H.string()])};function _p(e){return function({backend:t,session:n},r,i){return new s({async start(a){try{let o=await e(t).initContext(r,{...i,onProgress:e=>{a.enqueue({event:`progress`,data:{progress:e}})}});n.initializedContexts.add(r),await new Promise(e=>setTimeout(e,1e3));let{download:s,...c}=o||{};a.enqueue({event:`result`,data:{result:c}}),a.close()}catch(e){a.error(e)}}})}}function vp(e,t){return async function({backend:n,session:r},i,a){return console.log(`[Server] ${t}:`,{id:i,force:a}),r.initializedContexts.has(i)?(r.initializedContexts.delete(i),e(n).releaseContext(i,{force:a})):(console.log(`[Server] ${t} skipped - not initialized by this session:`,{id:i}),{released:!1,skipped:!0})}}function yp(e,t){return{initContext:_p(e),completion({backend:n},r,i){return console.log(`[Server] ${t}Completion:`,{id:r,property:i}),e(n).completion(r,i)},async tokenize({backend:n},r,i){return console.log(`[Server] ${t}Tokenize:`,{id:r,property:i}),e(n).tokenize(r,i)},async detokenize({backend:n},r,i){return console.log(`[Server] ${t}Detokenize:`,{id:r,property:i}),e(n).detokenize(r,i)},async applyChatTemplate({backend:n},r,i){return console.log(`[Server] ${t}Apply Chat Template:`,{id:r,property:i}),e(n).applyChatTemplate(r,i)},releaseContext:vp(e,`${t}Release Context`)}}const bp={...gp,embedding:H.tuple([H.string(),H.any()])},xp=e=>e.ggmlLlm;var Sp={...yp(xp,``),async embedding({backend:e},t,n){return console.log(`[Server] Embedding:`,{id:t,property:n}),xp(e).embedding(t,n)}};const Cp={initContext:H.tuple([H.string(),H.any().optional()]),transcribe:H.tuple([H.string(),H.string(),H.any().optional()]),transcribeData:H.tuple([H.string(),H.union([H.instanceof(Buffer),H.instanceof(Uint8Array)]),H.any().optional()]),releaseContext:H.tuple([H.string()])},wp=e=>e.ggmlStt;var Tp={initContext:_p(wp),async transcribe({backend:e,session:t},n,r,i){console.log(`[Server] Transcribe:`,{id:n,audioPath:r,options:i});let a=t.fileManager.resolve(r);if(!a)throw Error(`Audio file not found`);return e.ggmlStt.transcribe(n,{audioPath:a,options:i})},async transcribeData({backend:e},t,n,r){return console.log(`[Server] Transcribe Data:`,{id:t,audioDataLength:n?.length||0,options:r}),e.ggmlStt.transcribeData(t,{audioData:n,options:r})},releaseContext:vp(wp,`Release STT Context`)},Ep=yp(e=>e.mlxLlm,`MLX `);function Dp(e){return function({backend:t},n,r){return new s({async start(i){try{let a=await e(t).initContext(n,{...r,onProgress:e=>{i.enqueue({event:`progress`,data:{progress:e}})}});await new Promise(e=>setTimeout(e,1e3)),i.enqueue({event:`result`,data:{result:a}}),i.close()}catch(e){i.error(e)}}})}}const Op=H.object({language:H.string().optional(),task:H.string().optional(),return_timestamps:H.union([H.boolean(),H.literal(`word`)]).optional(),chunk_length_s:H.number().optional()}).passthrough(),kp=H.object({audio:H.string(),options:Op.optional()}),Ap={initContext:H.tuple([H.string(),H.record(H.unknown()).optional()]),transcribe:H.tuple([H.string(),kp]),transcribeData:H.tuple([H.string(),H.union([H.instanceof(Buffer),H.instanceof(Uint8Array)]),Op.optional()]),releaseContext:H.tuple([H.string()])};var jp={initContext:Dp(e=>e.onnxStt),transcribe({backend:e,session:t},n,r){let{audio:i,options:a}=r;console.log(`[Server] ONNX STT Transcribe:`,{id:n,audio:i,options:a});let o=t.fileManager.resolve(i);if(!o)throw Error(`Audio file not found`);return e.onnxStt.transcribe(n,{audio:o,options:a})},async transcribeData({backend:e},t,n,r){return console.log(`[Server] ONNX STT Transcribe Data:`,{id:t,audioDataLength:n?.length||0,options:r}),e.onnxStt.transcribeData(t,{audioData:n,options:r})},async releaseContext({backend:e},t){return e.onnxStt.releaseContext(t,{})}};const Mp=H.object({text:H.string(),options:H.object({speaker:H.string().optional()}).passthrough().optional()}),Np={initContext:H.tuple([H.string(),H.record(H.unknown()).optional()]),addSpeaker:H.tuple([H.string(),H.union([H.instanceof(Float32Array),H.record(H.unknown())])]),synthesize:H.tuple([H.string(),Mp]),releaseContext:H.tuple([H.string()])},Pp={common:hp,ggmlLlm:Sp,ggmlStt:Tp,mlxLlm:Ep,onnxStt:jp,onnxTts:{initContext:Dp(e=>e.onnxTts),async addSpeaker({backend:e},t,n){return e.onnxTts.addSpeaker(t,n)},async synthesize({backend:e,session:t},n,r){let i=await e.onnxTts.synthesize(n,r),a=`tts-${i.cachedId}.wav`;return t.fileManager.register(a,i.cachedFile),{filename:a,sampling_rate:i.sampling_rate,channels:i.channels}},async releaseContext({backend:e},t){return e.onnxTts.releaseContext(t,{})}}},Fp={common:dp,ggmlLlm:bp,ggmlStt:Cp,mlxLlm:gp,onnxStt:Ap,onnxTts:Np},Ip=e=>e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength),Lp=e=>{try{return JSON.parse(e,(e,t)=>{if(!t)return t;if(t?.type===`Buffer`&&t?.data||t?.type===`Uint8Array`&&typeof t?.data==`string`)return Ce.from(t.data,`base64`);if(t?.type===`Float32Array`&&typeof t?.data==`string`)return new Float32Array(Ip(Ce.from(t.data,`base64`)));if(t?.type===`Error`&&t?.name&&t?.message){let e=Error(t.message);return e.name=t.name,e}return t})}catch{return e}},Rp=e=>{try{return JSON.stringify(e,(e,t)=>t instanceof Error?{type:`Error`,name:t.name,message:t.message}:t instanceof Ce?{type:`Buffer`,data:t.toString(`base64`)}:t instanceof Float32Array?{type:`Float32Array`,data:Ce.from(t.buffer,t.byteOffset,t.byteLength).toString(`base64`)}:t instanceof Uint8Array?{type:`Uint8Array`,data:Ce.from(t).toString(`base64`)}:t)}catch{return e}},zp={score:0,hasGpu:!1,usableBytes:0},Bp=e=>e?{score:Number(e.score)||0,hasGpu:!!e.hasGpu,usableBytes:Number(e.gpuUsableBytes||e.cpuUsableBytes||0)}:zp,Vp=async e=>{let t=[];for(let n of e){let e=zp;try{let t=await tu(n.type,null,{});e=Bp(t?.buttress?.selected)}catch(e){console.warn(`[Caps] Failed to detect capabilities for "${n.type}":`,e instanceof Error?e.message:e)}t.push({...n,...e})}return t},Hp=()=>{let e=T.networkInterfaces();return Object.values(e).flat().find(e=>e?.family===`IPv4`&&!e?.internal)?.address||null},Up=e=>{let t=e.split(`.`).map(Number);return t.length!==4||t.some(e=>Number.isNaN(e))?0:(t[0]<<24|t[1]<<16|t[2]<<8|t[3])>>>0},Wp=e=>[e>>>24&255,e>>>16&255,e>>>8&255,e&255].join(`.`),Gp=()=>{let e=[],t=new Set;for(let n of Object.values(T.networkInterfaces()))for(let r of n??[]){if(r.family!==`IPv4`||r.internal||!r.address||!r.netmask)continue;let n=Up(r.address),i=Up(r.netmask);if(!n||!i||i===4294967295)continue;let a=Wp((n&i|~i>>>0)>>>0);if(a===r.address)continue;let o=`${r.address}->${a}`;t.has(o)||(t.add(o),e.push({address:r.address,broadcast:a}))}return e},Kp=e=>{if(!e)return!1;let t=e;return t.code===`ENOTSUP`||/Failed to bind socket/i.test(t.message??``)};var qp=class{name=`udp`;receiver=null;senders=[];announcementTimer=null;config;getServerInfo;port;signer;constructor(e,t,n){this.config=e,this.getServerInfo=t,this.port=e.port??8089,this.signer=n}async start(){this.receiver=await this.bindReceiver(!0).catch(e=>{if(!Kp(e))throw e;return console.warn(`[Autodiscover UDP] SO_REUSEPORT not supported by this runtime; falling back to REUSEADDR only (multiple buttress instances on one host will not coexist on the discovery port).`),this.bindReceiver(!1)}),this.receiver.on(`message`,(e,t)=>{this.handleMessage(e,t)}),this.receiver.on(`error`,e=>{console.error(`[Autodiscover UDP] Receiver error:`,e.message)}),this.receiver.setBroadcast(!0),this.senders=await this.createSenders();let e=this.senders.map(e=>`${e.address}->${e.broadcast}`).join(`, `)||`<none>`;if(console.log(`[Autodiscover UDP] Listening on port ${this.port}; announce interfaces: ${e}`),this.config.announcements.enabled){let e=this.config.announcements.interval??5e3;this.announcementTimer=setInterval(()=>{this.sendAnnouncement()},e),this.sendAnnouncement()}}async stop(){this.announcementTimer&&=(clearInterval(this.announcementTimer),null),await Promise.all(this.senders.map(({socket:e})=>new Promise(t=>{e.close(()=>t())}))),this.senders=[],this.receiver&&=(await new Promise(e=>{this.receiver.close(()=>e())}),null)}async bindReceiver(e){let t=we.createSocket(e?{type:`udp4`,reuseAddr:!0,reusePort:!0}:{type:`udp4`,reuseAddr:!0});return new Promise((e,n)=>{let r=e=>{t.close(),n(e)};t.once(`error`,r),t.bind(this.port,()=>{t.off(`error`,r),e(t)})})}async createSenders(){let e=Gp();return(await Promise.all(e.map(async e=>{try{let t=we.createSocket({type:`udp4`});return await new Promise((n,r)=>{let i=e=>{t.close(),r(e)};t.once(`error`,i),t.bind({port:0,address:e.address},()=>{t.off(`error`,i),t.setBroadcast(!0),n()})}),t.on(`error`,t=>{console.error(`[Autodiscover UDP] Sender ${e.address} error:`,t.message)}),{...e,socket:t}}catch(t){return console.warn(`[Autodiscover UDP] Failed to bind sender on ${e.address}:`,t.message),null}}))).filter(e=>e!==null)}handleMessage(e,t){try{let n=JSON.parse(e.toString());if(n.t===`QUERY`&&this.config.requests.enabled){let e=n.d,r=this.config.requests.responseDelay??0,i=r>0?Math.random()*r:0;setTimeout(()=>{this.sendResponse(e.id,t)},i)}}catch{}}sendAnnouncement(){if(this.senders.length===0)return;let e=this.getServerInfo(),t=wu(this.signer,`ANNOUNCE`,{info:e});if(!t){console.warn("[Autodiscover UDP] no per-server keypair; skipping announcement. Run `bricks buttress bind` to register a key.");return}let n=Buffer.from(JSON.stringify(t));for(let{broadcast:e,socket:t,address:r}of this.senders)t.send(n,0,n.length,this.port,e,t=>{t&&console.error(`[Autodiscover UDP] Announcement ${r}->${e} error:`,t.message)})}sendResponse(e,t){if(!this.receiver)return;let n=this.getServerInfo(),r=wu(this.signer,`RESPONSE`,{request_id:e,info:n});if(!r)return;let i=Buffer.from(JSON.stringify(r));this.receiver.send(i,0,i.length,t.port,t.address,e=>{e&&console.error(`[Autodiscover UDP] Response error:`,e.message)})}},Jp=class{config;getServerInfo;signer;transports=[];started=!1;constructor(e,t,n){this.config=e,this.getServerInfo=t,this.signer=n,(e.udp?.announcements?.enabled||e.udp?.requests?.enabled)&&this.transports.push(new qp(e.udp,t,n))}async start(){this.started||=((await Promise.allSettled(this.transports.map(e=>e.start()))).forEach((e,t)=>{e.status===`rejected`&&console.error(`[Autodiscover] Failed to start ${this.transports[t].name}:`,e.reason)}),!0)}async stop(){this.started&&=(await Promise.allSettled(this.transports.map(e=>e.stop())),!1)}},Yp=class{sessionId;sessionDir;registry=new Map;constructor(e,t){this.sessionId=e,this.sessionDir=w.join(t,e)}register(e,t,n={}){this.registry.set(e,{realPath:t,owned:n.owned??!1,ttl:n.ttl??null,registeredAt:Date.now()})}resolve(e){let t=this.registry.get(e);return t?t.ttl!==null&&Date.now()-t.registeredAt>t.ttl?(t.owned&&v(t.realPath).catch(()=>{}),this.registry.delete(e),null):t.realPath:null}async storeUpload(e,t){let n=e.replace(/[^\dA-Za-z]/g,`_`),r=`${Date.now()}-${n}`;await u(this.sessionDir,{recursive:!0});let i=w.join(this.sessionDir,r);return t instanceof Buffer?await y(i,t):t instanceof ArrayBuffer?await y(i,Buffer.from(t)):await xe(be.fromWeb(t),N(i)),this.register(r,i,{owned:!0}),r}async destroy(){let e=[...this.registry.values()].filter(e=>e.owned).map(e=>e.realPath);await Promise.all(e.map(e=>v(e).catch(()=>{}))),await g(this.sessionDir,{recursive:!0,force:!0}).catch(()=>{}),this.registry.clear()}};const Xp=()=>process.env.BRICKS_BUTTRESS_STATE_DIR||w.join(T.homedir(),`.bricks-cli`,`buttress`),Zp=()=>w.join(Xp(),`state.json`),Qp=e=>{if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.id==`string`&&typeof t.serverId==`string`&&typeof t.issuerPublicKey==`string`&&typeof t.kid==`string`},$p=e=>{if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.publicKeySpki==`string`&&typeof t.privateKeyPkcs8==`string`&&typeof t.kid==`string`},em=()=>{let e=Zp();try{let t=j.readFileSync(e,`utf8`),n=JSON.parse(t);return{workspace:Qp(n?.workspace)?n.workspace:null,serverKeyPair:$p(n?.serverKeyPair)?n.serverKeyPair:null}}catch(e){return e.code!==`ENOENT`&&console.warn(`[Buttress] Failed to read workspace state:`,e.message),{workspace:null,serverKeyPair:null}}},tm=`@fugood/buttress-server`,nm=process.platform===`win32`,rm=e=>process.env.BUTTRESS_CDN_BASE||`https://cdn.bricks.tools/bricks-buttress/${e}`,im=(e,t)=>`${rm(e)}/${t}`,am=e=>e===`beta`?`beta`:`latest`,om=e=>e.replace(/\.exe$/i,``),sm=e=>e?om(e.split(/[\\/]/).pop()||``):``,cm=e=>{if(!e)return e;try{return j.realpathSync(e)}catch{return e}},lm=(e,t)=>{if(!e)return null;let n=w.sep,r=`${n}node_modules${n}${t.replace(/\//g,n)}${n}`,i=e.indexOf(r);return i===-1?null:e.slice(0,i)},um=(e,t)=>{if(!t)return!1;let n=e.split(/[.-]/),r=t.split(/[.-]/);for(let e=0;e<Math.max(n.length,r.length);e+=1){let t=parseInt(n[e])||0,i=parseInt(r[e])||0;if(i>t)return!0;if(i<t)return!1}return!1},dm=({version:e}={})=>{let t=e||``;return/-(beta|alpha|rc|next)\b/i.test(t)?`beta`:`release`},fm=({execPath:e=process.execPath,scriptPath:t=process.argv[1],homeDir:n=T.homedir(),realpath:r=cm}={})=>{if(sm(e)===`bricks-buttress`)return{method:`binary`,execPath:e,installDir:w.dirname(e)};let i=r(t),a=lm(i,tm);if(a){let e=w.sep,t=w.join(n,`.bun`,`install`,`global`);return a===t||a.startsWith(t+e)?{method:`bun`,installRoot:a}:/[\\/](?:\.pnpm|pnpm[\\/]global)[\\/]/.test(a)?{method:`pnpm`,installRoot:a}:/[\\/](?:\.yarn|yarn[\\/]global)[\\/]/.test(a)?{method:`yarn`,installRoot:a}:{method:`npm`,installRoot:a.endsWith(`${e}lib`)?a.slice(0,-`${e}lib`.length):a}}return{method:`unknown`,execPath:e,scriptPath:i}},pm=async e=>{let t=await fetch(e);if(!t.ok)throw Error(`Failed to fetch ${e}: HTTP ${t.status}`);return t},mm=async e=>(await pm(im(e,`version.json`))).json(),hm=async e=>{let t=(await(await pm(`https://registry.npmjs.org/-/package/${tm}/dist-tags`)).json())[am(e)];if(!t)throw Error(`No '${am(e)}' dist-tag for ${tm}`);return{version:t}},gm=async(e,t)=>t===`npm`||t===`bun`?hm(e):mm(e),_m=(e,t)=>{let n=ie(e,t,{stdio:`inherit`});if(n.error)throw n.error.code===`ENOENT`?Error(`Command not found: ${e}`):n.error;if(n.status!==0)throw Error(`${e} exited with code ${n.status}`)},vm=async(e,t)=>{let n=await pm(e),r=Buffer.from(await n.arrayBuffer()),i=w.join(T.tmpdir(),`bricks-buttress-update-${process.pid}-${t}`);return await c.writeFile(i,r),nm||await c.chmod(i,493),i},ym=e=>{_m(`npm`,[`install`,`-g`,`${tm}@${am(e)}`])},bm=e=>{_m(`bun`,[`install`,`-g`,`${tm}@${am(e)}`])},xm=async(e,{installDir:t,ggmlVariant:n}={})=>{let r=nm?`install.ps1`:`install.sh`,i=await vm(im(e,r),r);try{let[r,a]=nm?[`powershell`,[`-NoProfile`,`-ExecutionPolicy`,`Bypass`,`-File`,i,...e===`beta`?[`-Beta`]:[],...t?[`-InstallDir`,t]:[],...n?[`-GgmlVariant`,n]:[]]]:[`sh`,[i,...e===`beta`?[`--beta`]:[],...t?[`--dir=${t}`]:[],...n?[`--ggml-variant=${n}`]:[]]];_m(r,a)}finally{await c.unlink(i).catch(()=>{})}},Sm=async(e,t,n={method:e},r={})=>{if(e===`npm`)return ym(t);if(e===`bun`)return bm(t);if(e===`binary`)return xm(t,{installDir:n.installDir,...r});throw Error(`Unsupported update method: ${e}`)},Cm=bu(),wm=(e,t)=>nu(e,t),Tm=(e,t)=>{e.push(`
|
|
1564
|
+
`)]),{seeded:t}},wf=async(e,t=864e5)=>{let n;try{n=await c.readdir(e,{withFileTypes:!0})}catch{return 0}let r=Date.now()-t,i=0;return await Promise.all(n.map(async t=>{if(!t.isDirectory())return;let n=w.join(e,t.name);try{if((await c.stat(n)).mtimeMs>=r)return;await c.rm(n,{recursive:!0,force:!0}),i+=1}catch{}})),i},Tf=e=>e===`~`||e.startsWith(`~/`)?w.join(T.homedir(),e.slice(1)):e,Ef=(e,t)=>{if(typeof e==`number`&&e>0)return e;if(typeof e==`string`){let t=W(e);if(typeof t==`number`&&t>0)return t}return t},Df=(e,{configDir:t=process.cwd(),env:n=process.env}={})=>{let r=e?.functions;if(!(n.ENABLE_FUNCTIONS_ENDPOINT===`1`||r?.enabled===!0))return null;let i=n.BUTTRESS_FUNCTIONS_DIR||r?.dir;if(!i||typeof i!=`string`)return console.warn(`[Functions] disabled: no directory configured. Set [functions] dir = "./functions" in your config (or BUTTRESS_FUNCTIONS_DIR).`),null;let a=Tf(i);return{enabled:!0,dir:w.isAbsolute(a)?a:w.resolve(t,a),allowUnauthenticated:n.BUTTRESS_FUNCTIONS_ALLOW_UNAUTHENTICATED===`1`||r?.allow_unauthenticated===!0,defaultTimeoutMs:Ef(r?.default_timeout,3e5),hotReload:n.BUTTRESS_FUNCTIONS_HOT_RELOAD===`1`||r?.hot_reload===!0,userConfig:r?.config&&typeof r.config==`object`?{...r.config}:{}}},Of=async(e,{tempFileDir:t})=>{console.log(`[Functions] Local functions are EXPERIMENTAL: endpoints, the file contract and the context API may change between releases.`);let{seeded:n}=await Cf(e.dir);n&&console.log(`[Functions] Prepared functions directory: ${e.dir}`);let r=await wf(w.join(t,`functions`));r>0&&console.log(`[Functions] Removed ${r} stale scratch director${r===1?`y`:`ies`}`);let i=Gd(e),a=Yd(e),o={enabled:!0,count:0},s=async()=>{let e=await i.list();return o.count=e.length,e},c=await s();console.log(`[Functions] Loaded ${c.length} function${c.length===1?``:`s`} from ${e.dir}`);try{let e=await a.get();if(e){let t=e.mode===`override`?`replaces workspace auth`:`runs after workspace auth`;console.log(`[Functions] Custom auth: ${w.basename(e.file)} (mode: ${e.mode} — ${t})`)}}catch{}let l=null;return e.hotReload&&(l=yf(e.dir,e=>{let t=[...e].slice(0,3).join(`, `);console.log(`[Functions] Change detected (${t}${e.size>3?`, …`:``}); reloading`),Promise.allSettled([s(),a.get()])}),l&&console.log(`[Functions] Hot reload: watching ${e.dir}`)),{dispose:()=>l?.close(),config:e,dir:e.dir,stats:o,list:s,describe:e=>i.describe(e),getCustomAuth:()=>a.get(),call:async(t,n,{runtime:r,emit:a,signal:o,callId:s,surface:c=`http`})=>{let l=Date.now();try{let u=await _f(await i.get(t),n,{runtime:r,functionsConfig:e,emit:a,signal:o,callId:s});return Hu.recordCall({name:t,surface:c,durationMs:Date.now()-l,success:!0}),u}catch(e){throw Hu.recordCall({name:t,surface:c,durationMs:Date.now()-l,success:!1,error:e?.name&&e.name!==`Error`?e.name:e?.message||String(e)}),e}}}},kf=256*1024,Af=e=>{if(e===void 0)return``;let t=typeof e==`string`?e:JSON.stringify(e,null,2)??String(e);return t.length<=kf?t:`${t.slice(0,kf)}\n… [truncated ${t.length-kf} characters]`},jf=({functions:e,runtime:t,version:n,signal:r})=>{let i=new _e({name:`buttress-functions`,version:n},{capabilities:{tools:{}}});return i.setRequestHandler(ye,async()=>({tools:(await e.list()).map(e=>({name:e.name,description:e.description,inputSchema:e.parameters}))})),i.setRequestHandler(ve,async n=>{let{name:i,arguments:a}=n.params;try{let n=await e.call(i,a??{},{runtime:t,signal:r,surface:`mcp`});return{content:[{type:`text`,text:Af(n)}]}}catch(e){return{content:[{type:`text`,text:`Error: ${e?.message||String(e)}`}],isError:!0}}}),i};var Mf=class{onmessage;onerror;onclose;responses=[];resolveResponse=null;async start(){}async send(e){if(this.resolveResponse){let t=this.resolveResponse;this.resolveResponse=null,t(e);return}this.responses.push(e)}async close(){this.onclose?.()}async exchange(e){let t=e?.id!==void 0&&e?.id!==null;if(t&&typeof e.method!=`string`)return{jsonrpc:`2.0`,id:e.id,error:{code:-32600,message:`Invalid Request`}};let n=t?new Promise(t=>{let n=this.responses.shift();if(n){t(n);return}this.resolveResponse=t,this.onerror=n=>{this.resolveResponse===t&&(this.resolveResponse=null,t({jsonrpc:`2.0`,id:e.id,error:{code:-32603,message:n?.message||`Internal error`}}))}}):Promise.resolve(null);return this.onmessage?.(e),n}};const Nf=async(e,t)=>{let n=jf(e),r=new Mf;try{if(await n.connect(r),Array.isArray(t)){let e=[];for(let n of t){let t=await r.exchange(n);t&&e.push(t)}return e.length>0?e:null}return await r.exchange(t)}finally{await n.close().catch(()=>{})}},Pf=(e,t,n)=>{Object.defineProperty(e,t,{configurable:!0,enumerable:!0,value:n,writable:!0})},Ff=e=>typeof e==`object`&&!!e&&typeof e.stream==`function`&&typeof e.arrayBuffer==`function`&&typeof e.name==`string`,If=async(e,t)=>{await c.mkdir(e,{recursive:!0});let n=ff(t.name);for(let r=0;r<100;r+=1){let i=r===0?n:`${r}-${n}`,a=w.join(e,i);try{await xe(be.fromWeb(t.stream()),N(a,{flags:`wx`}))}catch(e){if(e?.code===`EEXIST`)continue;throw e}return{path:a,name:i,size:(await c.stat(a)).size}}throw Error(`Could not find a free name for upload "${n}"`)};var Lf=class extends Error{constructor(e){super(e),this.name=`MultipartInputError`}};const Rf=async(e,t)=>{let n={},r=e.input;if(Ff(r))throw new Lf(`The "input" field must be a JSON object, not a file`);if(typeof r==`string`&&r.trim()!==``){let e;try{e=JSON.parse(r)}catch{throw new Lf(`The "input" field must be valid JSON`)}if(typeof e!=`object`||!e||Array.isArray(e))throw new Lf(`The "input" field must be a JSON object`);n=e}let i=[];for(let[r,a]of Object.entries(e)){if(r===`input`)continue;let e=(Array.isArray(a)?a:[a]).filter(Ff);if(e.length===0){typeof a==`string`&&Pf(n,r,a);continue}let o=[];for(let n of e){let e=await If(t,n);o.push(e),i.push({...e,field:r})}Pf(n,r,o.length===1?o[0].path:o.map(e=>e.path))}return{input:n,staged:i}};var zf=class extends Error{constructor(e){super(e),this.name=`QueryInputError`}};const Bf=new Set([`stream`,`token`,`access_token`]),Vf=e=>{if(typeof e!=`object`||!e)return;let{type:t}=e;if(typeof t==`string`)return t;if(Array.isArray(t))return t.find(e=>typeof e==`string`&&e!==`null`)},Hf=e=>{try{return JSON.parse(e)}catch{return}},Uf=(e,t)=>{switch(Vf(t)){case`number`:case`integer`:{if(e.trim()===``)return e;let t=Number(e);return Number.isFinite(t)?t:e}case`boolean`:{let t=e.toLowerCase();return t===``||t===`true`||t===`1`?!0:t===`false`||t===`0`?!1:e}case`object`:{let t=Hf(e);return typeof t==`object`&&t&&!Array.isArray(t)?t:e}case`array`:return Wf([e],t);default:return e}},Wf=(e,t)=>{let n=t?.items;if(e.length>1)return e.map(e=>Uf(e,n));let r=e[0];if(r===``)return[];if(r.startsWith(`[`)){let e=Hf(r);if(Array.isArray(e))return e}return r.split(`,`).map(e=>Uf(e,n))},Gf=(e,t)=>{let n={},r=e.get(`input`);if(r!=null&&r.trim()!==``){let e=Hf(r);if(e===void 0)throw new zf(`The "input" parameter must be valid JSON`);if(typeof e!=`object`||!e||Array.isArray(e))throw new zf(`The "input" parameter must be a JSON object`);n=e}let i=t?.properties??{};for(let t of new Set(e.keys())){if(t===`input`||Bf.has(t))continue;let r=e.getAll(t),a=i[t];Pf(n,t,Vf(a)===`array`||r.length>1?Wf(r,a):Uf(r[0],a))}return n},Kf=(e,t)=>({error:{code:e,message:t}}),qf=(e,t)=>{try{JSON.stringify(e)}catch(e){throw new Jf(t,e?.message||String(e))}};var Jf=class extends Error{constructor(e,t){super(`Function "${e}" returned a value that cannot be encoded as JSON: ${t}`),this.name=`UnserializableResultError`}};const Yf=e=>e instanceof Wd?{status:404,body:Kf(`FUNCTION_NOT_FOUND`,e.message)}:e instanceof mf?{status:504,body:Kf(`FUNCTION_TIMEOUT`,e.message)}:e instanceof Jf?{status:500,body:Kf(`FUNCTION_RESULT_NOT_SERIALIZABLE`,e.message)}:{status:500,body:Kf(`FUNCTION_FAILED`,e?.message||String(e))},Xf=(e,t)=>e.stream===`1`||e.stream===`true`||String(t?.accept||``).includes(`text/event-stream`);async function*Zf(e,t,n,r){let a=[],o=null,s=!1,c=()=>r.signal?.aborted===!0,l=(e,t)=>{a.push({event:e,data:t}),o?.()},u=e.call(t,n,{...r,emit:l}).then(e=>(qf(e,t),{ok:!0,value:e})).then(e=>e,e=>({ok:!1,error:e})).then(e=>(s=!0,o?.(),e));for(;;){for(;a.length>0;){let e=a.shift();if(c())return;yield i({event:`progress`,data:JSON.stringify(e)})}if(s)break;await new Promise(e=>{o=e,(a.length>0||s)&&e()}),o=null}let d=await u;if(c())return;if(d.ok){yield i({event:`result`,data:JSON.stringify({result:d.value})});return}let{body:f}=Yf(d.error);yield i({event:`error`,data:JSON.stringify(f)})}function Qf(e,t){let n=Su({prefix:`/functions`});n.use(ce({origin:e.global?.functions?.cors_allowed_origins??!1,methods:[`GET`,`POST`,`OPTIONS`],allowedHeaders:[`Content-Type`,`Authorization`,`Mcp-Session-Id`,`Mcp-Protocol-Version`],maxAge:86400,preflight:!0})),n.onBeforeHandle(Ed({allowUnauthenticated:t.config.allowUnauthenticated,allowedOrigins:e.global?.functions?.cors_allowed_origins,getCustomAuth:t.getCustomAuth}));let i=async({name:e,input:n,query:r,headers:i,store:a,set:o,request:s,callId:l,stagedDir:u})=>{let d={backend:a.backend,config:a.config};if(Xf(r,i))return Zf(t,e,n,{runtime:d,signal:s.signal,callId:l,surface:`sse`});try{let r=await t.call(e,n,{runtime:d,signal:s.signal,callId:l});return qf(r,e),{result:r}}catch(e){e instanceof Wd&&u&&await c.rm(u,{recursive:!0,force:!0}).catch(()=>{});let{status:t,body:n}=Yf(e);return o.status=t,n}};n.get(`/`,async()=>({functions:await t.list()})),n.post(`/mcp`,async({body:e,store:n,set:r,request:i})=>{let a=e;return!a||typeof a!=`object`?(r.status=400,Kf(`INVALID_REQUEST`,`Expected a JSON-RPC message body`)):await Nf({functions:t,runtime:{backend:n.backend,config:n.config},version:n.serverInfo.version,signal:i.signal},a)||(r.status=202,null)});let o=({set:e})=>(e.status=405,Kf(`METHOD_NOT_ALLOWED`,`This MCP endpoint only accepts POST`));return n.get(`/mcp`,o),n.delete(`/mcp`,o),n.get(`/files/*`,async({params:e,store:t,set:n})=>{let i=w.join(t.config.server.temp_file_dir,`functions`),a=e[`*`]??``,o=uf(i,a);if(o)try{let e=await c.stat(o);if(e.isFile())return Hu.recordDownload({path:w.relative(i,o),size:e.size,success:!0}),r(o)}catch{}return Hu.recordDownload({path:a,success:!1}),n.status=404,Kf(`FUNCTION_FILE_NOT_FOUND`,`No such function output file`)}),n.post(`/upload`,async({body:e,store:t,set:n})=>{let r=e.file,i=w.join(t.config.server.temp_file_dir,`functions`),a=w.join(i,`upload-${S()}`);try{let e=await If(a,r);return Hu.recordUpload({source:`upload`,name:e.name,size:e.size,success:!0}),{...e,url:df(i,e.path)}}catch(e){return await c.rm(a,{recursive:!0,force:!0}).catch(()=>{}),Hu.recordUpload({source:`upload`,name:typeof r?.name==`string`?r.name:`-`,size:typeof r?.size==`number`?r.size:0,success:!1,error:e?.message||String(e)}),n.status=500,Kf(`UPLOAD_FAILED`,e?.message||String(e))}},{body:a.Object({file:a.File()})}),n.post(`/:name`,async({params:e,body:t,query:n,headers:r,store:a,set:o,request:s})=>{let{name:l}=e,u=t??{},d,f;if(String(r[`content-type`]||r[`Content-Type`]||``).includes(`multipart/form-data`)){if(!yd.test(l))return o.status=404,Kf(`FUNCTION_NOT_FOUND`,`Unknown function "${l}"`);d=S();let e=w.join(a.config.server.temp_file_dir,`functions`),n=w.join(e,`${l}-${d}`);try{let e=await Rf(t,w.join(n,`uploads`));u=e.input,f=n;for(let t of e.staged)Hu.recordUpload({source:`call`,name:t.name,size:t.size,success:!0})}catch(e){return await c.rm(n,{recursive:!0,force:!0}).catch(()=>{}),Hu.recordUpload({source:`call`,name:`-`,size:0,success:!1,error:e?.message||String(e)}),e instanceof Lf?(o.status=400,Kf(`INVALID_INPUT`,e.message)):(o.status=500,Kf(`UPLOAD_FAILED`,e?.message||String(e)))}}return typeof u!=`object`||Array.isArray(u)?(o.status=400,Kf(`INVALID_INPUT`,`Function input must be a JSON object`)):i({name:l,input:u,query:n,headers:r,store:a,set:o,request:s,callId:d,stagedDir:f})}),n.get(`/:name`,async({params:e,query:n,headers:r,store:a,set:o,request:s})=>{let{name:c}=e,l=await t.describe(c).catch(()=>null),u;try{u=Gf(new URL(s.url).searchParams,l?.parameters)}catch(e){if(!(e instanceof zf))throw e;return o.status=400,Kf(`INVALID_INPUT`,e.message)}return o.headers&&(o.headers[`cache-control`]=`no-store`),i({name:c,input:u,query:n,headers:r,store:a,set:o,request:s})}),n}const $f=(e={},t={})=>{let n=Array.isArray(e)?[...e]:{...e};return Object.entries(t||{}).forEach(([e,t])=>{t&&typeof t==`object`&&!Array.isArray(t)?n[e]=$f(n[e]||{},t):n[e]=t}),n},ep=e=>e&&typeof e==`object`?structuredClone(e):null,tp=(e,t)=>{let n=ep(e)||{},r=ep(t)||{};return $f(n,r)},np=(e,t)=>$f(structuredClone(e.global),t||{}),rp=(e,t,n,r)=>{if(e.generators.length>0){let i=e.generators.filter(e=>e?.type===n);if(i.length>0&&r){let a=i.find(e=>t.getModelIdentifier(n,e)===r);if(a)return np(e,a)}}return Object.keys(e.global).length>0?np(e,{}):null},ip={udp:{port:8089,announcements:{enabled:!0,interval:5e3},requests:{enabled:!0,responseDelay:100}},http:{enabled:!0,path:`/buttress/info`,cors:!0}},ap=e=>e?e===!0?{...ip}:$f(ip,e):null,op=(e,t)=>{if(!e.generators||e.generators.length===0)return t.map(e=>({type:e}));let n=new Set;return e.generators.forEach(e=>{e.type&&n.add(e.type)}),n.size===0?t.map(e=>({type:e})):Array.from(n).map(e=>({type:e}))},sp=(e,t,n)=>e===void 0?n:typeof e==`number`?e:t(e)??n,cp=6e4,lp=1024*1024*50,up=e=>{let t=Se.machineIdSync(),n={server:{id:`buttress-${t}`,name:`Buttress Server (${t.slice(-8)})`,port:2080,temp_file_dir:w.join(T.tmpdir(),`.buttress`),session_timeout:cp,max_body_size:lp},autodiscover:!1},r=$f(n,ep(e)||{}),i=Array.isArray(r.generators)?r.generators:[],{server:a,generators:o,autodiscover:s,...c}=r;return{autodiscover:ap(s),server:{id:a.id,name:a.name,port:a.port,log_level:a.log_level,temp_file_dir:a.temp_file_dir,max_body_size:sp(a.max_body_size,ee.parse,lp),session_timeout:sp(a.session_timeout,e=>W(e),cp)},global:c,generators:i}},dp={getCapabilities:H.tuple([H.object({type:H.string().optional().default(`ggml-llm`),config:H.any().optional(),currentClientCapabilities:H.any().optional(),options:H.any().optional()}).nullable().optional()]),startGenerator:H.tuple([H.string(),H.any().optional()]),finalizeGenerator:H.tuple([H.string()])},fp=(e,t)=>!!(e&&Object.prototype.hasOwnProperty.call(e,t)),pp=e=>{let t=ep(e);if(!t)return null;let n=ep(t.runtime),r=ep(t.model);if(fp(n,`cache_dir`))return`runtime.cache_dir`;if(fp(r,`local_path`))return`model.local_path`;if(fp(r,`mmproj_local_path`))return`model.mmproj_local_path`;if(fp(r,`allow_local_file`))return`model.allow_local_file`;for(let[e,t]of[[`model`,r],[`runtime`,n]])for(let n of[`model_draft`,`draft_model`]){if(!fp(t,n))continue;let r=t?.[n];if(typeof r==`string`&&!/^https?:\/\//i.test(r)||fp(ep(r),`local_path`))return`${e}.${n}.local_path`}return null},mp=(e,t,n,r)=>{let i=pp(r);if(i)throw Error(`Remote generator config cannot override server filesystem path "${i}"`);let a=ep(r),o=tp(rp(e,t,n,t.getModelIdentifier(n,a)),r);if(Object.keys(o).length===0)throw Error(`Buttress server missing generator configuration`);return o.backend=o.backend||{},o.backend.type||(o.backend.type=n),o};var hp={async getCapabilities({backend:e,config:t},n=null){console.log(`[Server] Get Capabilities:`,n);let{type:r=`ggml-llm`,config:i,currentClientCapabilities:a=null,options:o={}}=n||{type:`ggml-llm`},s=mp(t,e,r,i);return e.getCapabilities(r,a,{...o,config:s})},async startGenerator({backend:e,config:t,session:n},r,i){console.log(`[Server] Start Generator:`,r,i);let a=mp(t,e,r,i),o=await e.startGenerator(r,a);return n.generators.set(o.id,(n.generators.get(o.id)??0)+1),o},async finalizeGenerator({backend:e,session:t},n){console.log(`[Server] Finalize Generator:`,n);let r=t.generators.get(n)??0;return r===0?!1:(r>1?t.generators.set(n,r-1):t.generators.delete(n),e.finalizeGenerator(n))}};const gp={initContext:H.tuple([H.string(),H.any().optional()]),completion:H.tuple([H.string(),H.any().optional()]),tokenize:H.tuple([H.string(),H.any()]),detokenize:H.tuple([H.string(),H.any()]),applyChatTemplate:H.tuple([H.string(),H.any()]),releaseContext:H.tuple([H.string()])};function _p(e){return function({backend:t,session:n},r,i){return new s({async start(a){try{let o=await e(t).initContext(r,{...i,onProgress:e=>{a.enqueue({event:`progress`,data:{progress:e}})}});n.initializedContexts.add(r),await new Promise(e=>setTimeout(e,1e3));let{download:s,...c}=o||{};a.enqueue({event:`result`,data:{result:c}}),a.close()}catch(e){a.error(e)}}})}}function vp(e,t){return async function({backend:n,session:r},i,a){return console.log(`[Server] ${t}:`,{id:i,force:a}),r.initializedContexts.has(i)?(r.initializedContexts.delete(i),e(n).releaseContext(i,{force:a})):(console.log(`[Server] ${t} skipped - not initialized by this session:`,{id:i}),{released:!1,skipped:!0})}}function yp(e,t){return{initContext:_p(e),completion({backend:n},r,i){return console.log(`[Server] ${t}Completion:`,{id:r,property:i}),e(n).completion(r,i)},async tokenize({backend:n},r,i){return console.log(`[Server] ${t}Tokenize:`,{id:r,property:i}),e(n).tokenize(r,i)},async detokenize({backend:n},r,i){return console.log(`[Server] ${t}Detokenize:`,{id:r,property:i}),e(n).detokenize(r,i)},async applyChatTemplate({backend:n},r,i){return console.log(`[Server] ${t}Apply Chat Template:`,{id:r,property:i}),e(n).applyChatTemplate(r,i)},releaseContext:vp(e,`${t}Release Context`)}}const bp={...gp,embedding:H.tuple([H.string(),H.any()])},xp=e=>e.ggmlLlm;var Sp={...yp(xp,``),async embedding({backend:e},t,n){return console.log(`[Server] Embedding:`,{id:t,property:n}),xp(e).embedding(t,n)}};const Cp={initContext:H.tuple([H.string(),H.any().optional()]),transcribe:H.tuple([H.string(),H.string(),H.any().optional()]),transcribeData:H.tuple([H.string(),H.union([H.instanceof(Buffer),H.instanceof(Uint8Array)]),H.any().optional()]),releaseContext:H.tuple([H.string()])},wp=e=>e.ggmlStt;var Tp={initContext:_p(wp),async transcribe({backend:e,session:t},n,r,i){console.log(`[Server] Transcribe:`,{id:n,audioPath:r,options:i});let a=t.fileManager.resolve(r);if(!a)throw Error(`Audio file not found`);return e.ggmlStt.transcribe(n,{audioPath:a,options:i})},async transcribeData({backend:e},t,n,r){return console.log(`[Server] Transcribe Data:`,{id:t,audioDataLength:n?.length||0,options:r}),e.ggmlStt.transcribeData(t,{audioData:n,options:r})},releaseContext:vp(wp,`Release STT Context`)},Ep=yp(e=>e.mlxLlm,`MLX `);function Dp(e){return function({backend:t},n,r){return new s({async start(i){try{let a=await e(t).initContext(n,{...r,onProgress:e=>{i.enqueue({event:`progress`,data:{progress:e}})}});await new Promise(e=>setTimeout(e,1e3)),i.enqueue({event:`result`,data:{result:a}}),i.close()}catch(e){i.error(e)}}})}}const Op=H.object({language:H.string().optional(),task:H.string().optional(),return_timestamps:H.union([H.boolean(),H.literal(`word`)]).optional(),chunk_length_s:H.number().optional()}).passthrough(),kp=H.object({audio:H.string(),options:Op.optional()}),Ap={initContext:H.tuple([H.string(),H.record(H.unknown()).optional()]),transcribe:H.tuple([H.string(),kp]),transcribeData:H.tuple([H.string(),H.union([H.instanceof(Buffer),H.instanceof(Uint8Array)]),Op.optional()]),releaseContext:H.tuple([H.string()])};var jp={initContext:Dp(e=>e.onnxStt),transcribe({backend:e,session:t},n,r){let{audio:i,options:a}=r;console.log(`[Server] ONNX STT Transcribe:`,{id:n,audio:i,options:a});let o=t.fileManager.resolve(i);if(!o)throw Error(`Audio file not found`);return e.onnxStt.transcribe(n,{audio:o,options:a})},async transcribeData({backend:e},t,n,r){return console.log(`[Server] ONNX STT Transcribe Data:`,{id:t,audioDataLength:n?.length||0,options:r}),e.onnxStt.transcribeData(t,{audioData:n,options:r})},async releaseContext({backend:e},t){return e.onnxStt.releaseContext(t,{})}};const Mp=H.object({text:H.string(),options:H.object({speaker:H.string().optional()}).passthrough().optional()}),Np={initContext:H.tuple([H.string(),H.record(H.unknown()).optional()]),addSpeaker:H.tuple([H.string(),H.union([H.instanceof(Float32Array),H.record(H.unknown())])]),synthesize:H.tuple([H.string(),Mp]),releaseContext:H.tuple([H.string()])},Pp={common:hp,ggmlLlm:Sp,ggmlStt:Tp,mlxLlm:Ep,onnxStt:jp,onnxTts:{initContext:Dp(e=>e.onnxTts),async addSpeaker({backend:e},t,n){return e.onnxTts.addSpeaker(t,n)},async synthesize({backend:e,session:t},n,r){let i=await e.onnxTts.synthesize(n,r),a=`tts-${i.cachedId}.wav`;return t.fileManager.register(a,i.cachedFile),{filename:a,sampling_rate:i.sampling_rate,channels:i.channels}},async releaseContext({backend:e},t){return e.onnxTts.releaseContext(t,{})}}},Fp={common:dp,ggmlLlm:bp,ggmlStt:Cp,mlxLlm:gp,onnxStt:Ap,onnxTts:Np},Ip=e=>e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength),Lp=e=>{try{return JSON.parse(e,(e,t)=>{if(!t)return t;if(t?.type===`Buffer`&&t?.data||t?.type===`Uint8Array`&&typeof t?.data==`string`)return Ce.from(t.data,`base64`);if(t?.type===`Float32Array`&&typeof t?.data==`string`)return new Float32Array(Ip(Ce.from(t.data,`base64`)));if(t?.type===`Error`&&t?.name&&t?.message){let e=Error(t.message);return e.name=t.name,e}return t})}catch{return e}},Rp=e=>{try{return JSON.stringify(e,(e,t)=>t instanceof Error?{type:`Error`,name:t.name,message:t.message}:t instanceof Ce?{type:`Buffer`,data:t.toString(`base64`)}:t instanceof Float32Array?{type:`Float32Array`,data:Ce.from(t.buffer,t.byteOffset,t.byteLength).toString(`base64`)}:t instanceof Uint8Array?{type:`Uint8Array`,data:Ce.from(t).toString(`base64`)}:t)}catch{return e}},zp={score:0,hasGpu:!1,usableBytes:0},Bp=e=>e?{score:Number(e.score)||0,hasGpu:!!e.hasGpu,usableBytes:Number(e.gpuUsableBytes||e.cpuUsableBytes||0)}:zp,Vp=async e=>{let t=[];for(let n of e){let e=zp;try{let t=await tu(n.type,null,{});e=Bp(t?.buttress?.selected)}catch(e){console.warn(`[Caps] Failed to detect capabilities for "${n.type}":`,e instanceof Error?e.message:e)}t.push({...n,...e})}return t},Hp=()=>{let e=T.networkInterfaces();return Object.values(e).flat().find(e=>e?.family===`IPv4`&&!e?.internal)?.address||null},Up=e=>{let t=e.split(`.`).map(Number);return t.length!==4||t.some(e=>Number.isNaN(e))?0:(t[0]<<24|t[1]<<16|t[2]<<8|t[3])>>>0},Wp=e=>[e>>>24&255,e>>>16&255,e>>>8&255,e&255].join(`.`),Gp=()=>{let e=[],t=new Set;for(let n of Object.values(T.networkInterfaces()))for(let r of n??[]){if(r.family!==`IPv4`||r.internal||!r.address||!r.netmask)continue;let n=Up(r.address),i=Up(r.netmask);if(!n||!i||i===4294967295)continue;let a=Wp((n&i|~i>>>0)>>>0);if(a===r.address)continue;let o=`${r.address}->${a}`;t.has(o)||(t.add(o),e.push({address:r.address,broadcast:a}))}return e},Kp=e=>{if(!e)return!1;let t=e;return t.code===`ENOTSUP`||/Failed to bind socket/i.test(t.message??``)};var qp=class{name=`udp`;receiver=null;senders=[];announcementTimer=null;config;getServerInfo;port;signer;constructor(e,t,n){this.config=e,this.getServerInfo=t,this.port=e.port??8089,this.signer=n}async start(){this.receiver=await this.bindReceiver(!0).catch(e=>{if(!Kp(e))throw e;return console.warn(`[Autodiscover UDP] SO_REUSEPORT not supported by this runtime; falling back to REUSEADDR only (multiple buttress instances on one host will not coexist on the discovery port).`),this.bindReceiver(!1)}),this.receiver.on(`message`,(e,t)=>{this.handleMessage(e,t)}),this.receiver.on(`error`,e=>{console.error(`[Autodiscover UDP] Receiver error:`,e.message)}),this.receiver.setBroadcast(!0),this.senders=await this.createSenders();let e=this.senders.map(e=>`${e.address}->${e.broadcast}`).join(`, `)||`<none>`;if(console.log(`[Autodiscover UDP] Listening on port ${this.port}; announce interfaces: ${e}`),this.config.announcements.enabled){let e=this.config.announcements.interval??5e3;this.announcementTimer=setInterval(()=>{this.sendAnnouncement()},e),this.sendAnnouncement()}}async stop(){this.announcementTimer&&=(clearInterval(this.announcementTimer),null),await Promise.all(this.senders.map(({socket:e})=>new Promise(t=>{e.close(()=>t())}))),this.senders=[],this.receiver&&=(await new Promise(e=>{this.receiver.close(()=>e())}),null)}async bindReceiver(e){let t=we.createSocket(e?{type:`udp4`,reuseAddr:!0,reusePort:!0}:{type:`udp4`,reuseAddr:!0});return new Promise((e,n)=>{let r=e=>{t.close(),n(e)};t.once(`error`,r),t.bind(this.port,()=>{t.off(`error`,r),e(t)})})}async createSenders(){let e=Gp();return(await Promise.all(e.map(async e=>{try{let t=we.createSocket({type:`udp4`});return await new Promise((n,r)=>{let i=e=>{t.close(),r(e)};t.once(`error`,i),t.bind({port:0,address:e.address},()=>{t.off(`error`,i),t.setBroadcast(!0),n()})}),t.on(`error`,t=>{console.error(`[Autodiscover UDP] Sender ${e.address} error:`,t.message)}),{...e,socket:t}}catch(t){return console.warn(`[Autodiscover UDP] Failed to bind sender on ${e.address}:`,t.message),null}}))).filter(e=>e!==null)}handleMessage(e,t){try{let n=JSON.parse(e.toString());if(n.t===`QUERY`&&this.config.requests.enabled){let e=n.d,r=this.config.requests.responseDelay??0,i=r>0?Math.random()*r:0;setTimeout(()=>{this.sendResponse(e.id,t)},i)}}catch{}}sendAnnouncement(){if(this.senders.length===0)return;let e=this.getServerInfo(),t=wu(this.signer,`ANNOUNCE`,{info:e});if(!t){console.warn("[Autodiscover UDP] no per-server keypair; skipping announcement. Run `bricks buttress bind` to register a key.");return}let n=Buffer.from(JSON.stringify(t));for(let{broadcast:e,socket:t,address:r}of this.senders)t.send(n,0,n.length,this.port,e,t=>{t&&console.error(`[Autodiscover UDP] Announcement ${r}->${e} error:`,t.message)})}sendResponse(e,t){if(!this.receiver)return;let n=this.getServerInfo(),r=wu(this.signer,`RESPONSE`,{request_id:e,info:n});if(!r)return;let i=Buffer.from(JSON.stringify(r));this.receiver.send(i,0,i.length,t.port,t.address,e=>{e&&console.error(`[Autodiscover UDP] Response error:`,e.message)})}},Jp=class{config;getServerInfo;signer;transports=[];started=!1;constructor(e,t,n){this.config=e,this.getServerInfo=t,this.signer=n,(e.udp?.announcements?.enabled||e.udp?.requests?.enabled)&&this.transports.push(new qp(e.udp,t,n))}async start(){this.started||=((await Promise.allSettled(this.transports.map(e=>e.start()))).forEach((e,t)=>{e.status===`rejected`&&console.error(`[Autodiscover] Failed to start ${this.transports[t].name}:`,e.reason)}),!0)}async stop(){this.started&&=(await Promise.allSettled(this.transports.map(e=>e.stop())),!1)}},Yp=class{sessionId;sessionDir;registry=new Map;uploadSeq=0;constructor(e,t){this.sessionId=e,this.sessionDir=w.join(t,e)}register(e,t,n={}){this.registry.set(e,{realPath:t,owned:n.owned??!1,ttl:n.ttl??null,registeredAt:Date.now()})}resolve(e){let t=this.registry.get(e);return t?t.ttl!==null&&Date.now()-t.registeredAt>t.ttl?(t.owned&&v(t.realPath).catch(()=>{}),this.registry.delete(e),null):t.realPath:null}async storeUpload(e,t){let n=e.replace(/[^\dA-Za-z]/g,`_`);this.uploadSeq+=1;let r=`${Date.now()}-${this.uploadSeq}-${n}`;await u(this.sessionDir,{recursive:!0});let i=w.join(this.sessionDir,r);return t instanceof Buffer?await y(i,t):t instanceof ArrayBuffer?await y(i,Buffer.from(t)):await xe(be.fromWeb(t),N(i)),this.register(r,i,{owned:!0}),r}async destroy(){let e=[...this.registry.values()].filter(e=>e.owned).map(e=>e.realPath);await Promise.all(e.map(e=>v(e).catch(()=>{}))),await g(this.sessionDir,{recursive:!0,force:!0}).catch(()=>{}),this.registry.clear()}};const Xp=()=>process.env.BRICKS_BUTTRESS_STATE_DIR||w.join(T.homedir(),`.bricks-cli`,`buttress`),Zp=()=>w.join(Xp(),`state.json`),Qp=e=>{if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.id==`string`&&typeof t.serverId==`string`&&typeof t.issuerPublicKey==`string`&&typeof t.kid==`string`},$p=e=>{if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.publicKeySpki==`string`&&typeof t.privateKeyPkcs8==`string`&&typeof t.kid==`string`},em=()=>{let e=Zp();try{let t=j.readFileSync(e,`utf8`),n=JSON.parse(t);return{workspace:Qp(n?.workspace)?n.workspace:null,serverKeyPair:$p(n?.serverKeyPair)?n.serverKeyPair:null}}catch(e){return e.code!==`ENOENT`&&console.warn(`[Buttress] Failed to read workspace state:`,e.message),{workspace:null,serverKeyPair:null}}},tm=`@fugood/buttress-server`,nm=process.platform===`win32`,rm=e=>process.env.BUTTRESS_CDN_BASE||`https://cdn.bricks.tools/bricks-buttress/${e}`,im=(e,t)=>`${rm(e)}/${t}`,am=e=>e===`beta`?`beta`:`latest`,om=e=>e.replace(/\.exe$/i,``),sm=e=>e?om(e.split(/[\\/]/).pop()||``):``,cm=e=>{if(!e)return e;try{return j.realpathSync(e)}catch{return e}},lm=(e,t)=>{if(!e)return null;let n=w.sep,r=`${n}node_modules${n}${t.replace(/\//g,n)}${n}`,i=e.indexOf(r);return i===-1?null:e.slice(0,i)},um=(e,t)=>{if(!t)return!1;let n=e.split(/[.-]/),r=t.split(/[.-]/);for(let e=0;e<Math.max(n.length,r.length);e+=1){let t=parseInt(n[e])||0,i=parseInt(r[e])||0;if(i>t)return!0;if(i<t)return!1}return!1},dm=({version:e}={})=>{let t=e||``;return/-(beta|alpha|rc|next)\b/i.test(t)?`beta`:`release`},fm=({execPath:e=process.execPath,scriptPath:t=process.argv[1],homeDir:n=T.homedir(),realpath:r=cm}={})=>{if(sm(e)===`bricks-buttress`)return{method:`binary`,execPath:e,installDir:w.dirname(e)};let i=r(t),a=lm(i,tm);if(a){let e=w.sep,t=w.join(n,`.bun`,`install`,`global`);return a===t||a.startsWith(t+e)?{method:`bun`,installRoot:a}:/[\\/](?:\.pnpm|pnpm[\\/]global)[\\/]/.test(a)?{method:`pnpm`,installRoot:a}:/[\\/](?:\.yarn|yarn[\\/]global)[\\/]/.test(a)?{method:`yarn`,installRoot:a}:{method:`npm`,installRoot:a.endsWith(`${e}lib`)?a.slice(0,-`${e}lib`.length):a}}return{method:`unknown`,execPath:e,scriptPath:i}},pm=async e=>{let t=await fetch(e);if(!t.ok)throw Error(`Failed to fetch ${e}: HTTP ${t.status}`);return t},mm=async e=>(await pm(im(e,`version.json`))).json(),hm=async e=>{let t=(await(await pm(`https://registry.npmjs.org/-/package/${tm}/dist-tags`)).json())[am(e)];if(!t)throw Error(`No '${am(e)}' dist-tag for ${tm}`);return{version:t}},gm=async(e,t)=>t===`npm`||t===`bun`?hm(e):mm(e),_m=(e,t)=>{let n=ie(e,t,{stdio:`inherit`});if(n.error)throw n.error.code===`ENOENT`?Error(`Command not found: ${e}`):n.error;if(n.status!==0)throw Error(`${e} exited with code ${n.status}`)},vm=async(e,t)=>{let n=await pm(e),r=Buffer.from(await n.arrayBuffer()),i=w.join(T.tmpdir(),`bricks-buttress-update-${process.pid}-${t}`);return await c.writeFile(i,r),nm||await c.chmod(i,493),i},ym=e=>{_m(`npm`,[`install`,`-g`,`${tm}@${am(e)}`])},bm=e=>{_m(`bun`,[`install`,`-g`,`${tm}@${am(e)}`])},xm=async(e,{installDir:t,ggmlVariant:n}={})=>{let r=nm?`install.ps1`:`install.sh`,i=await vm(im(e,r),r);try{let[r,a]=nm?[`powershell`,[`-NoProfile`,`-ExecutionPolicy`,`Bypass`,`-File`,i,...e===`beta`?[`-Beta`]:[],...t?[`-InstallDir`,t]:[],...n?[`-GgmlVariant`,n]:[]]]:[`sh`,[i,...e===`beta`?[`--beta`]:[],...t?[`--dir=${t}`]:[],...n?[`--ggml-variant=${n}`]:[]]];_m(r,a)}finally{await c.unlink(i).catch(()=>{})}},Sm=async(e,t,n={method:e},r={})=>{if(e===`npm`)return ym(t);if(e===`bun`)return bm(t);if(e===`binary`)return xm(t,{installDir:n.installDir,...r});throw Error(`Unsupported update method: ${e}`)},Cm=bu(),wm=(e,t)=>nu(e,t),Tm=(e,t)=>{e.push(`
|
|
1565
1565
|
### Command Used`);let n=process.argv.slice(2).join(` `);if(e.push(`\`\`\`bash\n${process.argv[0]} ${process.argv[1]} ${n}\n\`\`\``),e.push(`
|
|
1566
1566
|
### Package Information`),e.push(`- **Name:** ${Cm.name}`),e.push(`- **Version:** ${Cm.version}`),Cm.description&&e.push(`- **Description:** ${Cm.description}`),t&&Object.keys(t).length>0){e.push(`
|
|
1567
1567
|
### Configuration`),e.push(`<details>`),e.push(`<summary>Click to expand TOML configuration</summary>`),e.push("\n```toml");try{let n=Te.stringify(t);e.push(n)}catch{e.push(`# Error serializing config`),e.push(JSON.stringify(t,null,2))}e.push("```"),e.push(`</details>`)}},Em=e=>{if(!e)return{repoId:null,filename:null};let[t,n]=e.split(`:`);return{repoId:t,filename:n||null}};async function Dm({modelIds:e=[],defaultConfig:t=null}={}){let n=[];console.log(`${Cm.name} v${Cm.version}`),console.log(`Generating model capabilities comparison...
|
|
@@ -1660,4 +1660,4 @@ Examples:
|
|
|
1660
1660
|
bricks-buttress --test-caps ggml-stt --test-caps-model-id BricksDisplay/whisper-ggml:ggml-small.bin
|
|
1661
1661
|
bricks-buttress --test-caps mlx-llm --test-models-default
|
|
1662
1662
|
`),process.exit(0));let e=process.argv.findIndex(e=>e===`--port`||e===`-p`),t=e>=0?Number(process.argv[e+1]):void 0,n=process.argv.findIndex(e=>e===`--config`||e===`-c`),r=n>=0?process.argv[n+1]:null,i=null,a=process.cwd();if(r){let e;if(r.includes(`
|
|
1663
|
-
`))e=r;else{let t=w.resolve(r);a=w.dirname(t);try{e=j.readFileSync(t,`utf8`)}catch(e){console.error(`Failed to read Buttress config at ${t}:`,e),process.exit(1)}}try{let t=Te.parse(e);t.env&&typeof t.env==`object`&&(Object.entries(t.env).forEach(([e,t])=>{process.env[e]===void 0&&(process.env[e]=String(t))}),delete t.env),i=t}catch(e){console.error(`Failed to parse TOML config:`,e),process.exit(1)}}async function o(e){if(!e?.generators||!Array.isArray(e.generators))return;let t=e.generators.filter(e=>{if(!e.model?.download)return!1;let{type:t}=e;return!t||![`ggml-llm`,`ggml-stt`,`mlx-llm`,`onnx-stt`,`onnx-tts`].includes(t)?(console.warn(`[Download] Skipping unknown generator type: ${t}`),!1):!0});if(t.length===0)return;let{server:n,generators:r,...i}=e,a=t.map(e=>{let{type:t}=e,n=e.model?.repo_id;return console.log(`[Download] Starting pre-download for ${t}: ${n}`),_u(t,{...i,backend:e.backend||{},model:e.model||{},runtime:{...i.runtime,...e.runtime||{}}},{onProgress:()=>{},onComplete:({repoId:e,alreadyExists:t})=>{console.log(t?`[Download] Pre-download complete (already exists): ${e}`:`[Download] Pre-download complete: ${e}`)},onError:e=>{console.error(`[Download] Pre-download failed for ${n}:`,e.message)}})}),o=await Promise.all(a),s=o.filter(e=>e.started).length,c=o.filter(e=>e.alreadyExists).length,l=o.filter(e=>e.alreadyDownloading).length;console.log(`[Download] Pre-download summary: ${s} started, ${c} already exist, ${l} already downloading`)}let s=[`ggml-org/gpt-oss-20b-GGUF`,`ggml-org/gpt-oss-120b-GGUF`,`unsloth/Nemotron-3-Nano-30B-A3B-GGUF`,`unsloth/Qwen3-30B-A3B-Instruct-2507-GGUF`,`unsloth/Qwen3.5-27B-GGUF`,`unsloth/gemma-4-26B-A4B-it-GGUF`,`unsloth/gemma-4-31B-it-GGUF`,`unsloth/GLM-4.7-Flash-GGUF`,`DevQuasar/MiniMaxAI.MiniMax-M2.5-GGUF`,`bartowski/Mistral-Nemo-Instruct-2407-GGUF`,`mistralai/Magistral-Small-2509-GGUF`,`mistralai/Ministral-3-14B-Reasoning-2512-GGUF`,`bartowski/mistralai_Devstral-Small-2-24B-Instruct-2512-GGUF`,`bartowski/mistralai_Devstral-2-123B-Instruct-2512-GGUF`,`ggml-org/gemma-3-12b-it-qat-GGUF`,`ggml-org/gemma-3-27b-it-qat-GGUF`,`unsloth/phi-4-GGUF`],c=[`BricksDisplay/whisper-ggml:ggml-small.bin`,`BricksDisplay/whisper-ggml:ggml-small-q8_0.bin`,`BricksDisplay/whisper-ggml:ggml-medium.bin`,`BricksDisplay/whisper-ggml:ggml-medium-q8_0.bin`,`BricksDisplay/whisper-ggml:ggml-large-v3-turbo.bin`,`BricksDisplay/whisper-ggml:ggml-large-v3-turbo-q8_0.bin`,`BricksDisplay/whisper-ggml:ggml-large-v3.bin`],l=[`mlx-community/Qwen3.5-27B-8bit`,`mlx-community/Qwen3.5-27B-4bit`,`mlx-community/Qwen3.5-35B-A3B-8bit`,`mlx-community/Qwen3.5-35B-A3B-4bit`,`mlx-community/Qwen3-235B-A22B-8bit`,`mlx-community/Qwen3-235B-A22B-4bit`,`mlx-community/GLM-4.7-Flash-8bit`,`mlx-community/GLM-4.7-Flash-4bit`,`mlx-community/MiniMax-M2.5-4bit`,`mlx-community/gpt-oss-120b-4bit`,`mlx-community/gemma-4-26b-a4b-it-8bit`,`mlx-community/gemma-4-26b-a4b-it-4bit`,`mlx-community/gemma-4-31b-it-8bit`,`mlx-community/gemma-4-31b-it-4bit`],u=process.argv.findIndex(e=>e===`--test-caps`);if(u>=0){let e=process.argv[u+1]||`ggml-llm`;e!==`ggml-llm`&&e!==`ggml-stt`&&e!==`mlx-llm`&&(console.error(`Only ggml-llm, ggml-stt, and mlx-llm backends are supported for testing capabilities`),process.exit(1));let t=process.argv.findIndex(e=>e===`--test-models`),n=process.argv.includes(`--test-models-default`);if(e===`mlx-llm`)if(t>=0){let e=process.argv[t+1];e||(console.error(`Error: --test-models requires a comma-separated list of model IDs`),process.exit(1)),await Am({modelIds:e.split(`,`).map(e=>e.trim()),defaultConfig:i})}else if(n)await Am({modelIds:l,defaultConfig:i});else{let e=process.argv.findIndex(e=>e===`--test-caps-model-id`);await jm({modelId:e>=0?process.argv[e+1]:null,defaultConfig:i})}else if(e===`ggml-stt`)if(t>=0){let e=process.argv[t+1];e||(console.error(`Error: --test-models requires a comma-separated list of model IDs`),process.exit(1)),await km({modelIds:e.split(`,`).map(e=>e.trim()),defaultConfig:i})}else if(n)await km({modelIds:c,defaultConfig:i});else{let e=process.argv.findIndex(e=>e===`--test-caps-model-id`);await Mm({modelId:e>=0?process.argv[e+1]:null,defaultConfig:i})}else if(t>=0){let e=process.argv[t+1];e||(console.error(`Error: --test-models requires a comma-separated list of model IDs`),process.exit(1)),await Dm({modelIds:e.split(`,`).map(e=>e.trim()),defaultConfig:i})}else if(n)await Dm({modelIds:s,defaultConfig:i});else{let e=process.argv.findIndex(e=>e===`--test-caps-model-id`);await Om({modelId:e>=0?process.argv[e+1]:null,defaultConfig:i})}}let d=up(i);t&&(d.server.port=t),d.server.port||(d.server.port=2080),Wm({config:d,enableOpenAICompat:process.env.ENABLE_OPENAI_COMPAT_ENDPOINT===`1`||d.global.openai_compat?.enabled===!0,enableAnthropicMessages:process.env.ENABLE_ANTHROPIC_MESSAGES_ENDPOINT===`1`||d.global.anthropic_messages?.enabled===!0,functions:Df(d.global,{configDir:a})}).then(async({app:e,port:t,openaiEnabled:n,anthropicMessagesEnabled:r,functions:a,autoDiscover:s})=>{let c=Hp();console.log(`Buttress server listening on port ${t}`),console.log(`--------------------------------`),await Vm(),console.log();let l=e.store.workspaceState.workspace;if(l){let e=l.name?`${l.name} (${l.id})`:l.id;console.log(`Workspace: ${e}`),console.log(`- Server ID: ${l.serverId}`),console.log(`- Issuer kid: ${l.kid}`),console.log(`- Bound at: ${l.boundAt}`)}else console.log(`Workspace: not bound`),console.log(`- State file: ${Zp()}`),console.log("- Run `bricks buttress bind` from a workspace-authed CLI to pair.");console.log();let u={"ggml-llm":`LLM (GGML)`,"ggml-stt":`STT (GGML)`,"mlx-llm":`LLM (MLX)`,"onnx-stt":`STT (ONNX)`,"onnx-tts":`TTS (ONNX)`};console.log(`Current supported Generators:`);let f=new Set((d?.generators||[]).map(e=>e.type).filter(Boolean));if(f.size===0)console.log(`- LLM (GGML)`),console.log(`- STT (GGML)`);else for(let e of f)console.log(`- ${u[e]||e}`);if(console.log(),console.log("Please configure `Buttress (Remote Inference)` in the Generator to connect to this server."),console.log(),console.log(`- Use http://${c}:${t} to connect to this server via LAN.`),console.log(`- Visit http://${c}:${t}/status to see status via LAN.`),console.log(),n?(console.log(`OpenAI-compatible API [EXPERIMENTAL]:`),console.log(`- Base URL: http://${c}:${t}/oai-compat/v1`),console.log(`- Chat completions: POST http://${c}:${t}/oai-compat/v1/chat/completions`),console.log(`- Models: GET http://${c}:${t}/oai-compat/v1/models`),console.log()):(console.log(`OpenAI-compatible API [EXPERIMENTAL]: disabled`),console.log(` Set [openai_compat] enabled = true in config to enable`),console.log()),r?(console.log(`Anthropic Messages API [EXPERIMENTAL]:`),console.log(`- Base URL: http://${c}:${t}/anthropic-messages`),console.log(`- Messages: POST http://${c}:${t}/anthropic-messages/v1/messages`),console.log(`- Count tokens: POST http://${c}:${t}/anthropic-messages/v1/messages/count_tokens`),console.log()):(console.log(`Anthropic Messages API [EXPERIMENTAL]: disabled`),console.log(` Set [anthropic_messages] enabled = true in config to enable`),console.log()),a){let n=await a.list();console.log(`Local Functions [EXPERIMENTAL]:`),console.log(`- Experimental: endpoints and the function contract may change between releases.`),console.log(`- Directory: ${a.dir}`),console.log(`- List: GET http://${c}:${t}/functions`),console.log(`- Call: POST http://${c}:${t}/functions/<name>`),console.log(`- MCP: POST http://${c}:${t}/functions/mcp`),console.log(`- Loaded: ${n.length?n.map(e=>e.name).join(`, `):`(none yet)`}`),a.config.hotReload&&console.log(`- Hot reload: watching the functions directory for changes`);let r=null,i=!1;try{r=await a.getCustomAuth()}catch{i=!0}if(i)console.warn(`- WARNING: the custom auth file (_auth) failed to load. Every call is rejected until it is fixed.`);else if(r){let e=r.mode===`override`?`replaces workspace auth`:`extra gate after workspace auth`;console.log(`- Custom auth: ${w.basename(r.file)} (${r.mode} — ${e})`)}let o=!i&&r?.mode!==`override`;!e.store.workspaceState.workspace&&o&&(a.config.allowUnauthenticated?console.warn(r?`- Unauthenticated access is ON and this server is not bound; the custom auth function is the only gate on these functions.`:`- WARNING: unauthenticated access is ON and this server is not bound to a workspace. Anyone who can reach this port can run these functions.`):console.log("- Calls are rejected until this server is bound (`bricks buttress bind`), or [functions] allow_unauthenticated = true is set.")),console.log()}else console.log(`Local Functions [EXPERIMENTAL]: disabled`),console.log(` Set [functions] enabled = true and dir = "./functions" in config to enable`),console.log();s&&(console.log(`Auto-discover enabled`),console.log()),i&&await o(i)}).catch(e=>{console.error(`Failed to start Buttress server:`,e),process.exitCode=1})};const{version:Lm,name:Rm}=bu(),zm=async()=>{let e=`https://registry.npmjs.org/${Rm}/latest`;try{let t=new AbortController,n=setTimeout(()=>t.abort(),3e3),r=await fetch(e,{headers:{Accept:`application/json`},signal:t.signal});return clearTimeout(n),r.ok&&(await r.json()).version||null}catch{return null}},Bm=e=>{console.log(``),console.log(`\x1B[33m╭─────────────────────────────────────────────────╮\x1B[0m`),console.log(`\x1B[33m│\x1B[0m Update available! \x1B[2m%s\x1B[0m → \x1B[32m%s\x1B[0m`,Lm.padEnd(12),e.padEnd(12),`\x1B[33m│\x1B[0m`),console.log(`\x1B[33m│\x1B[0m \x1B[33m│\x1B[0m`),console.log(`\x1B[33m│\x1B[0m Run to upgrade: \x1B[33m│\x1B[0m`),console.log(`\x1B[33m│\x1B[0m \x1B[36m%s\x1B[0m\x1B[33m│\x1B[0m`,`bricks-buttress update`.padEnd(47)),console.log(`\x1B[33m╰─────────────────────────────────────────────────╯\x1B[0m`),console.log(``)},Vm=async()=>{try{let e=await zm();e&&um(Lm,e)&&Bm(e)}catch{}},Hm=!(`Bun`in globalThis),Um=async({backend:e,router:r,config:i,enableOpenAICompat:o,enableAnthropicMessages:l,functions:u})=>{try{await c.mkdir(i.server.temp_file_dir,{recursive:!0})}catch{}let d=Hp()||`0.0.0.0`,f=[...new Set([d,...Gp().map(e=>e.address)].filter(Boolean))],p=em(),m=p.workspace!=null,h=await Vp(op(i,i.generators.map(e=>e.type))),g=null;if(u)try{g=await Of(u,{tempFileDir:i.server.temp_file_dir})}catch(e){console.error(`[Functions] disabled: failed to initialize ${u.dir}:`,e?.message||e)}let _={id:i.server.id,name:i.server.name,version:Lm,address:d,addresses:f,port:i.server.port,url:`http://${d}:${i.server.port}`,generators:h,authentication:m?{required:!0,type:`workspace-jwt`,kid:p.workspace.kid,bound:!0}:{required:!1,type:`workspace-jwt`,bound:!1},...m?{workspace:{id:p.workspace.id,name:p.workspace.name}}:{},...g?{functions:g.stats}:{}},v=new n({serve:{maxRequestBodySize:i.server.max_body_size},websocket:{idleTimeout:Math.ceil(i.server.session_timeout/1e3)},adapter:Hm?t():void 0}).state({sessions:new Map,backend:e||ru,config:i,serverInfo:_,workspaceState:p});r&&v.use(r),i.autodiscover?.http?.enabled&&v.use(ku(i,Tu(p))),v.use(Bu),v.use(Ju),o&&v.use(dd(i)),l&&v.use(_d(i)),g&&v.use(Qf(i,g));let y=(e,t)=>Pu(Nu(e,t),p.workspace),b={INVALID_REQUEST:-32600,INVALID_PARAMS:-32602,METHOD_NOT_FOUND:-32601,INTERNAL_ERROR:-32603},x=e=>e.id??e.raw?.id??e.remoteAddress??`unknown-peer`,C=new Map,w=new Map;return v.ws(`/buttress/rpc`,{parse:(e,t)=>{if(typeof t==`string`)try{return JSON.parse(t)}catch{return e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.INVALID_REQUEST,message:`Invalid request`},id:null})),null}return t},body:a.Object({jsonrpc:a.String(),method:a.String(),params:a.String(),id:a.String()}),async open(e){let t=x(e),n=e.data?.headers,r=e.data?.query,i,a=new Promise(e=>{i=e});if(w.set(t,{identity:null,ready:a}),!m){console.log(`[Request] New connection: ${t} (unbound, no auth)`),i(!0);return}let o=await y(n||{},r);if(!o){console.warn(`[Auth] Rejecting WS ${t}: invalid or missing workspace-access token`),w.delete(t),i(!1),e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.INVALID_REQUEST,message:`Unauthorized`},id:null})),e.close(1008,`UNAUTHORIZED`);return}console.log(`[Request] New connection: ${t} (subject=${o.subjectType}:${o.subjectId})`);let s=w.get(t);s&&(s.identity=o),i(!0)},async message(e,{id:t,method:n,params:r}){let{sessions:a}=e.data.store,o=x(e),c=w.get(o);if(c?.ready&&!await c.ready){e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.INVALID_REQUEST,message:`Unauthorized`},id:t}));return}let l=w.get(o)?.identity??null;if(m&&!l){e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.INVALID_REQUEST,message:`Unauthorized`},id:t}));return}if(n===`init`){let[n]=Lp(r)??[],s,c=!1,u=n&&a.has(n)?a.get(n):null,d=!u?.identity||!!l&&l.subjectId===u.identity.subjectId&&l.subjectType===u.identity.subjectType;if(u&&d)u.timeout&&=(clearTimeout(u.timeout),null),u.identity=l,u.currentPeerId=o,s=n,c=!0,console.log(`[Request] Session restored: ${s}`);else{u&&console.warn(`[Request] Refused to restore session ${n}: identity mismatch (session=${u.identity?.subjectType}:${u.identity?.subjectId} peer=${l?.subjectType}:${l?.subjectId})`),s=S();let e={streams:new Map,streamReaders:new Map,generators:new Set,initializedContexts:new Set,fileManager:new Yp(s,i.server.temp_file_dir),timeout:null,identity:l,currentPeerId:o};a.set(s,e),console.log(`[Request] New session: ${s}`)}C.set(o,s),e.send(JSON.stringify({jsonrpc:`2.0`,result:Rp({sessionId:s,restored:c}),id:t}));return}let u=C.get(o);if(!u){e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.INVALID_REQUEST,message:`Session not initialized`},id:t}));return}let d=a.get(u);if(!d){C.delete(o),e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.INVALID_REQUEST,message:`Session not initialized`},id:t}));return}console.log(`[Request] Received request from ${u}: ${n}`);let[f,p]=n.split(`.`),h=Pp[f]?.[p];if(!h&&n!==`cancel`&&n!==`ping`){e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.METHOD_NOT_FOUND,message:`Method not found`},id:t}));return}try{if(n===`cancel`){let e=d.streamReaders.get(t);e&&(e.reader.cancel(),d.streamReaders.delete(t));return}if(n===`ping`){e.send(JSON.stringify({jsonrpc:`2.0`,result:`pong`,id:t}));return}let i=Lp(r),a=Fp[f]?.[p],c=a?a.parse(i):i,l=await h({...e.data.store,peerId:u,session:d},...c);if(l instanceof s){let r=l.getReader();d.streamReaders.set(t,{reader:r,peerId:o}),e.send(JSON.stringify({jsonrpc:`2.0`,result:{type:`stream`},id:t}));try{for(;;){let{value:n,done:i}=await r.read();if(i)break;let{event:a,data:o}=n;e.send(JSON.stringify({jsonrpc:`2.0`,method:`notification/${a}`,params:Rp(o),id:t}))}e.send(JSON.stringify({jsonrpc:`2.0`,method:`notification/_end`,id:t}))}catch(r){console.error(`[RPC] Stream error for ${n}:`,r),e.send(JSON.stringify({jsonrpc:`2.0`,method:`notification/_error`,params:Rp(r),id:t}))}d.streamReaders.delete(t)}else e.send(JSON.stringify({jsonrpc:`2.0`,result:Rp(l),id:t}))}catch(r){if(r instanceof V){e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.INVALID_PARAMS,message:`Invalid params`,data:r.issues},id:t}));return}console.error(`[RPC] Handler error for ${n}:`,r),e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.INTERNAL_ERROR,message:String(r)},id:t}))}},async close(e){let t=x(e),n=C.get(t);if(C.delete(t),w.delete(t),!n){console.log(`[Request] Connection closed (no session)`);return}console.log(`[Request] Connection closed: ${n}`);let{backend:r,sessions:a}=e.data.store,o=a.get(n);if(o){for(let[e,n]of o.streamReaders)n.peerId===t&&(n.reader.cancel().catch(()=>{}),o.streamReaders.delete(e));if(o.currentPeerId!==t){console.log(`[Request] Session ${n} already adopted by another peer; skip arm`);return}o.timeout=setTimeout(()=>{if(o.currentPeerId!==t)return;a.delete(n),console.log(`[Request] Session timed out: ${n}`);let{generators:e}=o;e.forEach(e=>{r.finalizeGenerator(e)}),o.fileManager.destroy().catch(()=>{})},i.server.session_timeout)}}}),{app:v,config:i,functions:g}},Wm=async({backend:e,router:t,config:n,enableOpenAICompat:r=!1,enableAnthropicMessages:i=!1,functions:a=null})=>{let{app:o,config:s,functions:c}=await Um({backend:e,router:t,config:n,enableOpenAICompat:r,enableAnthropicMessages:i,functions:a}),{server:{port:l}}=s,u=[new Promise(e=>o.listen(l,e))],d=null;if(s.autodiscover){let{workspace:e,serverKeyPair:t}=o.store.workspaceState;if(e&&t){let e=Tu(o.store.workspaceState);d=new Jp(s.autodiscover,()=>o.store.serverInfo,e),u.push(d.start())}else console.warn(e?"[Autodiscover] disabled: bound to a workspace but state.json is missing serverKeyPair. Re-run `bricks buttress bind` to register a per-server announce key (required for v2.0 signed UDP discovery).":"[Autodiscover] disabled: buttress-server is not bound to a workspace. Run `bricks buttress bind` from a workspace-authed CLI to pair.")}return await Promise.all(u),{app:o,port:l,openaiEnabled:r,anthropicMessagesEnabled:i,functions:c,autoDiscover:d}},Gm=[new URL(`index.mjs`,import.meta.url).pathname,new URL(`index.ts`,import.meta.url).pathname];(process.argv[1]?.endsWith(`/bricks-buttress`)||Gm.includes(process.argv[1]))&&await Im();export{Vm as checkAndNotifyUpdates,zm as checkForUpdates,um as compareVersions,Um as createServer,Bm as logUpdateMessage,up as processConfig,Df as resolveFunctionsConfig,_u as startModelDownload,Wm as startServer};
|
|
1663
|
+
`))e=r;else{let t=w.resolve(r);a=w.dirname(t);try{e=j.readFileSync(t,`utf8`)}catch(e){console.error(`Failed to read Buttress config at ${t}:`,e),process.exit(1)}}try{let t=Te.parse(e);t.env&&typeof t.env==`object`&&(Object.entries(t.env).forEach(([e,t])=>{process.env[e]===void 0&&(process.env[e]=String(t))}),delete t.env),i=t}catch(e){console.error(`Failed to parse TOML config:`,e),process.exit(1)}}async function o(e){if(!e?.generators||!Array.isArray(e.generators))return;let t=e.generators.filter(e=>{if(!e.model?.download)return!1;let{type:t}=e;return!t||![`ggml-llm`,`ggml-stt`,`mlx-llm`,`onnx-stt`,`onnx-tts`].includes(t)?(console.warn(`[Download] Skipping unknown generator type: ${t}`),!1):!0});if(t.length===0)return;let{server:n,generators:r,...i}=e,a=t.map(e=>{let{type:t}=e,n=e.model?.repo_id;return console.log(`[Download] Starting pre-download for ${t}: ${n}`),_u(t,{...i,backend:e.backend||{},model:e.model||{},runtime:{...i.runtime,...e.runtime||{}}},{onProgress:()=>{},onComplete:({repoId:e,alreadyExists:t})=>{console.log(t?`[Download] Pre-download complete (already exists): ${e}`:`[Download] Pre-download complete: ${e}`)},onError:e=>{console.error(`[Download] Pre-download failed for ${n}:`,e.message)}})}),o=await Promise.all(a),s=o.filter(e=>e.started).length,c=o.filter(e=>e.alreadyExists).length,l=o.filter(e=>e.alreadyDownloading).length;console.log(`[Download] Pre-download summary: ${s} started, ${c} already exist, ${l} already downloading`)}let s=[`ggml-org/gpt-oss-20b-GGUF`,`ggml-org/gpt-oss-120b-GGUF`,`unsloth/Nemotron-3-Nano-30B-A3B-GGUF`,`unsloth/Qwen3-30B-A3B-Instruct-2507-GGUF`,`unsloth/Qwen3.5-27B-GGUF`,`unsloth/gemma-4-26B-A4B-it-GGUF`,`unsloth/gemma-4-31B-it-GGUF`,`unsloth/GLM-4.7-Flash-GGUF`,`DevQuasar/MiniMaxAI.MiniMax-M2.5-GGUF`,`bartowski/Mistral-Nemo-Instruct-2407-GGUF`,`mistralai/Magistral-Small-2509-GGUF`,`mistralai/Ministral-3-14B-Reasoning-2512-GGUF`,`bartowski/mistralai_Devstral-Small-2-24B-Instruct-2512-GGUF`,`bartowski/mistralai_Devstral-2-123B-Instruct-2512-GGUF`,`ggml-org/gemma-3-12b-it-qat-GGUF`,`ggml-org/gemma-3-27b-it-qat-GGUF`,`unsloth/phi-4-GGUF`],c=[`BricksDisplay/whisper-ggml:ggml-small.bin`,`BricksDisplay/whisper-ggml:ggml-small-q8_0.bin`,`BricksDisplay/whisper-ggml:ggml-medium.bin`,`BricksDisplay/whisper-ggml:ggml-medium-q8_0.bin`,`BricksDisplay/whisper-ggml:ggml-large-v3-turbo.bin`,`BricksDisplay/whisper-ggml:ggml-large-v3-turbo-q8_0.bin`,`BricksDisplay/whisper-ggml:ggml-large-v3.bin`],l=[`mlx-community/Qwen3.5-27B-8bit`,`mlx-community/Qwen3.5-27B-4bit`,`mlx-community/Qwen3.5-35B-A3B-8bit`,`mlx-community/Qwen3.5-35B-A3B-4bit`,`mlx-community/Qwen3-235B-A22B-8bit`,`mlx-community/Qwen3-235B-A22B-4bit`,`mlx-community/GLM-4.7-Flash-8bit`,`mlx-community/GLM-4.7-Flash-4bit`,`mlx-community/MiniMax-M2.5-4bit`,`mlx-community/gpt-oss-120b-4bit`,`mlx-community/gemma-4-26b-a4b-it-8bit`,`mlx-community/gemma-4-26b-a4b-it-4bit`,`mlx-community/gemma-4-31b-it-8bit`,`mlx-community/gemma-4-31b-it-4bit`],u=process.argv.findIndex(e=>e===`--test-caps`);if(u>=0){let e=process.argv[u+1]||`ggml-llm`;e!==`ggml-llm`&&e!==`ggml-stt`&&e!==`mlx-llm`&&(console.error(`Only ggml-llm, ggml-stt, and mlx-llm backends are supported for testing capabilities`),process.exit(1));let t=process.argv.findIndex(e=>e===`--test-models`),n=process.argv.includes(`--test-models-default`);if(e===`mlx-llm`)if(t>=0){let e=process.argv[t+1];e||(console.error(`Error: --test-models requires a comma-separated list of model IDs`),process.exit(1)),await Am({modelIds:e.split(`,`).map(e=>e.trim()),defaultConfig:i})}else if(n)await Am({modelIds:l,defaultConfig:i});else{let e=process.argv.findIndex(e=>e===`--test-caps-model-id`);await jm({modelId:e>=0?process.argv[e+1]:null,defaultConfig:i})}else if(e===`ggml-stt`)if(t>=0){let e=process.argv[t+1];e||(console.error(`Error: --test-models requires a comma-separated list of model IDs`),process.exit(1)),await km({modelIds:e.split(`,`).map(e=>e.trim()),defaultConfig:i})}else if(n)await km({modelIds:c,defaultConfig:i});else{let e=process.argv.findIndex(e=>e===`--test-caps-model-id`);await Mm({modelId:e>=0?process.argv[e+1]:null,defaultConfig:i})}else if(t>=0){let e=process.argv[t+1];e||(console.error(`Error: --test-models requires a comma-separated list of model IDs`),process.exit(1)),await Dm({modelIds:e.split(`,`).map(e=>e.trim()),defaultConfig:i})}else if(n)await Dm({modelIds:s,defaultConfig:i});else{let e=process.argv.findIndex(e=>e===`--test-caps-model-id`);await Om({modelId:e>=0?process.argv[e+1]:null,defaultConfig:i})}}let d=up(i);t&&(d.server.port=t),d.server.port||(d.server.port=2080),Wm({config:d,enableOpenAICompat:process.env.ENABLE_OPENAI_COMPAT_ENDPOINT===`1`||d.global.openai_compat?.enabled===!0,enableAnthropicMessages:process.env.ENABLE_ANTHROPIC_MESSAGES_ENDPOINT===`1`||d.global.anthropic_messages?.enabled===!0,functions:Df(d.global,{configDir:a})}).then(async({app:e,port:t,openaiEnabled:n,anthropicMessagesEnabled:r,functions:a,autoDiscover:s})=>{let c=Hp();console.log(`Buttress server listening on port ${t}`),console.log(`--------------------------------`),await Vm(),console.log();let l=e.store.workspaceState.workspace;if(l){let e=l.name?`${l.name} (${l.id})`:l.id;console.log(`Workspace: ${e}`),console.log(`- Server ID: ${l.serverId}`),console.log(`- Issuer kid: ${l.kid}`),console.log(`- Bound at: ${l.boundAt}`)}else console.log(`Workspace: not bound`),console.log(`- State file: ${Zp()}`),console.log("- Run `bricks buttress bind` from a workspace-authed CLI to pair.");console.log();let u={"ggml-llm":`LLM (GGML)`,"ggml-stt":`STT (GGML)`,"mlx-llm":`LLM (MLX)`,"onnx-stt":`STT (ONNX)`,"onnx-tts":`TTS (ONNX)`};console.log(`Current supported Generators:`);let f=new Set((d?.generators||[]).map(e=>e.type).filter(Boolean));if(f.size===0)console.log(`- LLM (GGML)`),console.log(`- STT (GGML)`);else for(let e of f)console.log(`- ${u[e]||e}`);if(console.log(),console.log("Please configure `Buttress (Remote Inference)` in the Generator to connect to this server."),console.log(),console.log(`- Use http://${c}:${t} to connect to this server via LAN.`),console.log(`- Visit http://${c}:${t}/status to see status via LAN.`),console.log(),n?(console.log(`OpenAI-compatible API [EXPERIMENTAL]:`),console.log(`- Base URL: http://${c}:${t}/oai-compat/v1`),console.log(`- Chat completions: POST http://${c}:${t}/oai-compat/v1/chat/completions`),console.log(`- Models: GET http://${c}:${t}/oai-compat/v1/models`),console.log()):(console.log(`OpenAI-compatible API [EXPERIMENTAL]: disabled`),console.log(` Set [openai_compat] enabled = true in config to enable`),console.log()),r?(console.log(`Anthropic Messages API [EXPERIMENTAL]:`),console.log(`- Base URL: http://${c}:${t}/anthropic-messages`),console.log(`- Messages: POST http://${c}:${t}/anthropic-messages/v1/messages`),console.log(`- Count tokens: POST http://${c}:${t}/anthropic-messages/v1/messages/count_tokens`),console.log()):(console.log(`Anthropic Messages API [EXPERIMENTAL]: disabled`),console.log(` Set [anthropic_messages] enabled = true in config to enable`),console.log()),a){let n=await a.list();console.log(`Local Functions [EXPERIMENTAL]:`),console.log(`- Experimental: endpoints and the function contract may change between releases.`),console.log(`- Directory: ${a.dir}`),console.log(`- List: GET http://${c}:${t}/functions`),console.log(`- Call: POST http://${c}:${t}/functions/<name>`),console.log(`- MCP: POST http://${c}:${t}/functions/mcp`),console.log(`- Loaded: ${n.length?n.map(e=>e.name).join(`, `):`(none yet)`}`),a.config.hotReload&&console.log(`- Hot reload: watching the functions directory for changes`);let r=null,i=!1;try{r=await a.getCustomAuth()}catch{i=!0}if(i)console.warn(`- WARNING: the custom auth file (_auth) failed to load. Every call is rejected until it is fixed.`);else if(r){let e=r.mode===`override`?`replaces workspace auth`:`extra gate after workspace auth`;console.log(`- Custom auth: ${w.basename(r.file)} (${r.mode} — ${e})`)}let o=!i&&r?.mode!==`override`;!e.store.workspaceState.workspace&&o&&(a.config.allowUnauthenticated?console.warn(r?`- Unauthenticated access is ON and this server is not bound; the custom auth function is the only gate on these functions.`:`- WARNING: unauthenticated access is ON and this server is not bound to a workspace. Anyone who can reach this port can run these functions.`):console.log("- Calls are rejected until this server is bound (`bricks buttress bind`), or [functions] allow_unauthenticated = true is set.")),console.log()}else console.log(`Local Functions [EXPERIMENTAL]: disabled`),console.log(` Set [functions] enabled = true and dir = "./functions" in config to enable`),console.log();s&&(console.log(`Auto-discover enabled`),console.log()),i&&await o(i)}).catch(e=>{console.error(`Failed to start Buttress server:`,e),process.exitCode=1})};const{version:Lm,name:Rm}=bu(),zm=async()=>{let e=`https://registry.npmjs.org/${Rm}/latest`;try{let t=new AbortController,n=setTimeout(()=>t.abort(),3e3),r=await fetch(e,{headers:{Accept:`application/json`},signal:t.signal});return clearTimeout(n),r.ok&&(await r.json()).version||null}catch{return null}},Bm=e=>{console.log(``),console.log(`\x1B[33m╭─────────────────────────────────────────────────╮\x1B[0m`),console.log(`\x1B[33m│\x1B[0m Update available! \x1B[2m%s\x1B[0m → \x1B[32m%s\x1B[0m`,Lm.padEnd(12),e.padEnd(12),`\x1B[33m│\x1B[0m`),console.log(`\x1B[33m│\x1B[0m \x1B[33m│\x1B[0m`),console.log(`\x1B[33m│\x1B[0m Run to upgrade: \x1B[33m│\x1B[0m`),console.log(`\x1B[33m│\x1B[0m \x1B[36m%s\x1B[0m\x1B[33m│\x1B[0m`,`bricks-buttress update`.padEnd(47)),console.log(`\x1B[33m╰─────────────────────────────────────────────────╯\x1B[0m`),console.log(``)},Vm=async()=>{try{let e=await zm();e&&um(Lm,e)&&Bm(e)}catch{}},Hm=!(`Bun`in globalThis),Um=async({backend:e,router:r,config:i,enableOpenAICompat:o,enableAnthropicMessages:l,functions:u})=>{try{await c.mkdir(i.server.temp_file_dir,{recursive:!0})}catch{}let d=Hp()||`0.0.0.0`,f=[...new Set([d,...Gp().map(e=>e.address)].filter(Boolean))],p=em(),m=p.workspace!=null,h=await Vp(op(i,i.generators.map(e=>e.type))),g=null;if(u)try{g=await Of(u,{tempFileDir:i.server.temp_file_dir})}catch(e){console.error(`[Functions] disabled: failed to initialize ${u.dir}:`,e?.message||e)}let _={id:i.server.id,name:i.server.name,version:Lm,address:d,addresses:f,port:i.server.port,url:`http://${d}:${i.server.port}`,generators:h,authentication:m?{required:!0,type:`workspace-jwt`,kid:p.workspace.kid,bound:!0}:{required:!1,type:`workspace-jwt`,bound:!1},...m?{workspace:{id:p.workspace.id,name:p.workspace.name}}:{},...g?{functions:g.stats}:{}},v=new n({serve:{maxRequestBodySize:i.server.max_body_size},websocket:{idleTimeout:Math.ceil(i.server.session_timeout/1e3)},adapter:Hm?t():void 0}).state({sessions:new Map,backend:e||ru,config:i,serverInfo:_,workspaceState:p});r&&v.use(r),i.autodiscover?.http?.enabled&&v.use(ku(i,Tu(p))),v.use(Bu),v.use(Ju),o&&v.use(dd(i)),l&&v.use(_d(i)),g&&v.use(Qf(i,g));let y=(e,t)=>Pu(Nu(e,t),p.workspace),b={INVALID_REQUEST:-32600,INVALID_PARAMS:-32602,METHOD_NOT_FOUND:-32601,INTERNAL_ERROR:-32603},x=e=>e.id??e.raw?.id??e.remoteAddress??`unknown-peer`,C=new Map,w=new Map;return v.ws(`/buttress/rpc`,{parse:(e,t)=>{if(typeof t==`string`)try{return JSON.parse(t)}catch{return e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.INVALID_REQUEST,message:`Invalid request`},id:null})),null}return t},body:a.Object({jsonrpc:a.String(),method:a.String(),params:a.String(),id:a.String()}),async open(e){let t=x(e),n=e.data?.headers,r=e.data?.query,i,a=new Promise(e=>{i=e});if(w.set(t,{identity:null,ready:a}),!m){console.log(`[Request] New connection: ${t} (unbound, no auth)`),i(!0);return}let o=await y(n||{},r);if(!o){console.warn(`[Auth] Rejecting WS ${t}: invalid or missing workspace-access token`),w.delete(t),i(!1),e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.INVALID_REQUEST,message:`Unauthorized`},id:null})),e.close(1008,`UNAUTHORIZED`);return}console.log(`[Request] New connection: ${t} (subject=${o.subjectType}:${o.subjectId})`);let s=w.get(t);s&&(s.identity=o),i(!0)},async message(e,{id:t,method:n,params:r}){let{sessions:a}=e.data.store,o=x(e),c=w.get(o);if(c?.ready&&!await c.ready){e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.INVALID_REQUEST,message:`Unauthorized`},id:t}));return}let l=w.get(o)?.identity??null;if(m&&!l){e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.INVALID_REQUEST,message:`Unauthorized`},id:t}));return}if(n===`init`){let[n]=Lp(r)??[],s,c=!1,u=n&&a.has(n)?a.get(n):null,d=!u?.identity||!!l&&l.subjectId===u.identity.subjectId&&l.subjectType===u.identity.subjectType;if(u&&d)u.timeout&&=(clearTimeout(u.timeout),null),u.identity=l,u.currentPeerId=o,s=n,c=!0,console.log(`[Request] Session restored: ${s}`);else{u&&console.warn(`[Request] Refused to restore session ${n}: identity mismatch (session=${u.identity?.subjectType}:${u.identity?.subjectId} peer=${l?.subjectType}:${l?.subjectId})`),s=S();let e={streams:new Map,streamReaders:new Map,generators:new Map,initializedContexts:new Set,fileManager:new Yp(s,i.server.temp_file_dir),timeout:null,identity:l,currentPeerId:o};a.set(s,e),console.log(`[Request] New session: ${s}`)}C.set(o,s),e.send(JSON.stringify({jsonrpc:`2.0`,result:Rp({sessionId:s,restored:c}),id:t}));return}let u=C.get(o);if(!u){e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.INVALID_REQUEST,message:`Session not initialized`},id:t}));return}let d=a.get(u);if(!d){C.delete(o),e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.INVALID_REQUEST,message:`Session not initialized`},id:t}));return}console.log(`[Request] Received request from ${u}: ${n}`);let[f,p]=n.split(`.`),h=Pp[f]?.[p];if(!h&&n!==`cancel`&&n!==`ping`){e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.METHOD_NOT_FOUND,message:`Method not found`},id:t}));return}try{if(n===`cancel`){let e=d.streamReaders.get(t);e&&(e.reader.cancel(),d.streamReaders.delete(t));return}if(n===`ping`){e.send(JSON.stringify({jsonrpc:`2.0`,result:`pong`,id:t}));return}let i=Lp(r),a=Fp[f]?.[p],c=a?a.parse(i):i,l=await h({...e.data.store,peerId:u,session:d},...c);if(l instanceof s){let r=l.getReader();d.streamReaders.set(t,{reader:r,peerId:o}),e.send(JSON.stringify({jsonrpc:`2.0`,result:{type:`stream`},id:t}));try{for(;;){let{value:n,done:i}=await r.read();if(i)break;let{event:a,data:o}=n;e.send(JSON.stringify({jsonrpc:`2.0`,method:`notification/${a}`,params:Rp(o),id:t}))}e.send(JSON.stringify({jsonrpc:`2.0`,method:`notification/_end`,id:t}))}catch(r){console.error(`[RPC] Stream error for ${n}:`,r),e.send(JSON.stringify({jsonrpc:`2.0`,method:`notification/_error`,params:Rp(r),id:t}))}d.streamReaders.delete(t)}else e.send(JSON.stringify({jsonrpc:`2.0`,result:Rp(l),id:t}))}catch(r){if(r instanceof V){e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.INVALID_PARAMS,message:`Invalid params`,data:r.issues},id:t}));return}console.error(`[RPC] Handler error for ${n}:`,r),e.send(JSON.stringify({jsonrpc:`2.0`,error:{code:b.INTERNAL_ERROR,message:String(r)},id:t}))}},async close(e){let t=x(e),n=C.get(t);if(C.delete(t),w.delete(t),!n){console.log(`[Request] Connection closed (no session)`);return}console.log(`[Request] Connection closed: ${n}`);let{backend:r,sessions:a}=e.data.store,o=a.get(n);if(o){for(let[e,n]of o.streamReaders)n.peerId===t&&(n.reader.cancel().catch(()=>{}),o.streamReaders.delete(e));if(o.currentPeerId!==t){console.log(`[Request] Session ${n} already adopted by another peer; skip arm`);return}o.timeout=setTimeout(()=>{if(o.currentPeerId!==t)return;a.delete(n),console.log(`[Request] Session timed out: ${n}`);let{generators:e}=o;e.forEach((e,t)=>{for(let n=0;n<e;n++)r.finalizeGenerator(t)}),o.fileManager.destroy().catch(()=>{})},i.server.session_timeout)}}}),{app:v,config:i,functions:g}},Wm=async({backend:e,router:t,config:n,enableOpenAICompat:r=!1,enableAnthropicMessages:i=!1,functions:a=null})=>{let{app:o,config:s,functions:c}=await Um({backend:e,router:t,config:n,enableOpenAICompat:r,enableAnthropicMessages:i,functions:a}),{server:{port:l}}=s,u=[new Promise(e=>o.listen(l,e))],d=null;if(s.autodiscover){let{workspace:e,serverKeyPair:t}=o.store.workspaceState;if(e&&t){let e=Tu(o.store.workspaceState);d=new Jp(s.autodiscover,()=>o.store.serverInfo,e),u.push(d.start())}else console.warn(e?"[Autodiscover] disabled: bound to a workspace but state.json is missing serverKeyPair. Re-run `bricks buttress bind` to register a per-server announce key (required for v2.0 signed UDP discovery).":"[Autodiscover] disabled: buttress-server is not bound to a workspace. Run `bricks buttress bind` from a workspace-authed CLI to pair.")}return await Promise.all(u),{app:o,port:l,openaiEnabled:r,anthropicMessagesEnabled:i,functions:c,autoDiscover:d}},Gm=[new URL(`index.mjs`,import.meta.url).pathname,new URL(`index.ts`,import.meta.url).pathname];(process.argv[1]?.endsWith(`/bricks-buttress`)||Gm.includes(process.argv[1]))&&await Im();export{Vm as checkAndNotifyUpdates,zm as checkForUpdates,um as compareVersions,Um as createServer,Bm as logUpdateMessage,up as processConfig,Df as resolveFunctionsConfig,_u as startModelDownload,Wm as startServer};
|
package/lib/types.d.ts
CHANGED
|
@@ -147,7 +147,7 @@ export type StreamReaderEntry = {
|
|
|
147
147
|
export type Session = {
|
|
148
148
|
streams: Map<string, ReadableStream<EventStream>>;
|
|
149
149
|
streamReaders: Map<string, StreamReaderEntry>;
|
|
150
|
-
generators:
|
|
150
|
+
generators: Map<string, number>;
|
|
151
151
|
fileManager: import('./utils/SessionFileManager').SessionFileManager;
|
|
152
152
|
initializedContexts: Set<string>;
|
|
153
153
|
timeout: ReturnType<typeof setTimeout> | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fugood/buttress-server",
|
|
3
|
-
"version": "2.25.1-beta.
|
|
3
|
+
"version": "2.25.1-beta.5",
|
|
4
4
|
"main": "lib/index.mjs",
|
|
5
5
|
"types": "lib/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -69,5 +69,5 @@
|
|
|
69
69
|
"tsdown": "^0.22.4",
|
|
70
70
|
"typescript": "^7.0.2"
|
|
71
71
|
},
|
|
72
|
-
"gitHead": "
|
|
72
|
+
"gitHead": "48c91ea8d51ff6a53ad5c207da91cd77b929c9c5"
|
|
73
73
|
}
|