typebulb 0.40.0 → 0.41.0
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/README.md +5 -3
- package/dist/agents/claude/client.js +11 -9
- package/dist/agents/pi/client.js +11 -9
- package/dist/agents/pi/matchu-patchu.ts +1 -1
- package/dist/dts/tbTypings.d.ts +4 -3
- package/dist/dts/tbTypings.d.ts.map +1 -1
- package/dist/dts/tbTypings.js +21 -6
- package/dist/dts/tbTypings.js.map +1 -1
- package/dist/index.js +56 -50
- package/dist/render.js +11 -9
- package/dist/servers.js +12 -10
- package/package.json +1 -1
package/dist/render.js
CHANGED
|
@@ -618,12 +618,12 @@ const something = require('module-name') // NOT SUPPORTED!
|
|
|
618
618
|
}
|
|
619
619
|
};
|
|
620
620
|
|
|
621
|
-
// Diagnostic logging (tb.
|
|
621
|
+
// Diagnostic logging (tb.log). Routes to the UNGATED /__log endpoint, which only ever
|
|
622
622
|
// runs the server's built-in console.log \u2014 never a user server.ts export \u2014 so it crosses no
|
|
623
623
|
// capability boundary and works even on a Restricted (untrusted) bulb (the FAQ's recommended
|
|
624
624
|
// debugging path). Embedded bulbs have no host, and any transport failure degrades to the page's
|
|
625
625
|
// own console: a diagnostic log must never throw or block.
|
|
626
|
-
const
|
|
626
|
+
const tbLog = async (...args) => {
|
|
627
627
|
if (isEmbedded) { console.log(...args); return; }
|
|
628
628
|
try {
|
|
629
629
|
const resp = await fetch('/__log', {
|
|
@@ -851,17 +851,19 @@ const something = require('module-name') // NOT SUPPORTED!
|
|
|
851
851
|
return location.origin && location.origin !== 'null' ? location.origin + rel : rel;
|
|
852
852
|
},
|
|
853
853
|
|
|
854
|
+
// The bulb's log channel: prints on the CLI's stdout (\`typebulb logs\` reads it back). Ungated \u2014
|
|
855
|
+
// no server.ts, no trust \u2014 see tbLog above.
|
|
856
|
+
log: (...args) => { void tbLog(...args); },
|
|
857
|
+
|
|
854
858
|
// Server API - call functions from **server.ts**. Returns the hybrid call object: await it for
|
|
855
859
|
// a normal export's result, or for-await it for an async-generator export's stream.
|
|
856
860
|
api: (name, ...args) => serverCall(name, args),
|
|
857
861
|
|
|
858
|
-
// Server proxy - tb.server.fn(...) delegates to serverCall
|
|
859
|
-
//
|
|
860
|
-
//
|
|
862
|
+
// Server proxy - tb.server.fn(...) delegates to serverCall: purely the bulb's own server.ts
|
|
863
|
+
// exports, every one a gated /__api call. (The ungated diagnostic lives at tb.log, so no
|
|
864
|
+
// built-in shadows a user export.)
|
|
861
865
|
server: new Proxy({}, {
|
|
862
|
-
get: (_, name) => name
|
|
863
|
-
? (...args) => serverLog(...args)
|
|
864
|
-
: (...args) => serverCall(name, args)
|
|
866
|
+
get: (_, name) => (...args) => serverCall(name, args)
|
|
865
867
|
}),
|
|
866
868
|
|
|
867
869
|
// Filesystem - local CLI extension
|
|
@@ -876,7 +878,7 @@ const something = require('module-name') // NOT SUPPORTED!
|
|
|
876
878
|
},
|
|
877
879
|
|
|
878
880
|
// Receive a value pushed from the terminal via \`typebulb send\` (data-in, the dual of the
|
|
879
|
-
// ungated tb.
|
|
881
|
+
// ungated tb.log). Returns an unsubscribe fn. Inert when embedded \u2014 no server, so no
|
|
880
882
|
// sender; the handler is registered but never fires (cf. tb.models returning []). A handler's
|
|
881
883
|
// non-undefined return (awaited) becomes the reply \`send --wait\` prints (TB-Interrogation.md).
|
|
882
884
|
onMessage: (handler) => {
|
package/dist/servers.js
CHANGED
|
@@ -415,12 +415,12 @@ ${s}`}function Ff(t){let e=ur.filter(n=>t[n]).map(n=>zv(n,t[n]));return["---",`f
|
|
|
415
415
|
}
|
|
416
416
|
};
|
|
417
417
|
|
|
418
|
-
// Diagnostic logging (tb.
|
|
418
|
+
// Diagnostic logging (tb.log). Routes to the UNGATED /__log endpoint, which only ever
|
|
419
419
|
// runs the server's built-in console.log \u2014 never a user server.ts export \u2014 so it crosses no
|
|
420
420
|
// capability boundary and works even on a Restricted (untrusted) bulb (the FAQ's recommended
|
|
421
421
|
// debugging path). Embedded bulbs have no host, and any transport failure degrades to the page's
|
|
422
422
|
// own console: a diagnostic log must never throw or block.
|
|
423
|
-
const
|
|
423
|
+
const tbLog = async (...args) => {
|
|
424
424
|
if (isEmbedded) { console.log(...args); return; }
|
|
425
425
|
try {
|
|
426
426
|
const resp = await fetch('/__log', {
|
|
@@ -648,17 +648,19 @@ ${s}`}function Ff(t){let e=ur.filter(n=>t[n]).map(n=>zv(n,t[n]));return["---",`f
|
|
|
648
648
|
return location.origin && location.origin !== 'null' ? location.origin + rel : rel;
|
|
649
649
|
},
|
|
650
650
|
|
|
651
|
+
// The bulb's log channel: prints on the CLI's stdout (\`typebulb logs\` reads it back). Ungated \u2014
|
|
652
|
+
// no server.ts, no trust \u2014 see tbLog above.
|
|
653
|
+
log: (...args) => { void tbLog(...args); },
|
|
654
|
+
|
|
651
655
|
// Server API - call functions from **server.ts**. Returns the hybrid call object: await it for
|
|
652
656
|
// a normal export's result, or for-await it for an async-generator export's stream.
|
|
653
657
|
api: (name, ...args) => serverCall(name, args),
|
|
654
658
|
|
|
655
|
-
// Server proxy - tb.server.fn(...) delegates to serverCall
|
|
656
|
-
//
|
|
657
|
-
//
|
|
659
|
+
// Server proxy - tb.server.fn(...) delegates to serverCall: purely the bulb's own server.ts
|
|
660
|
+
// exports, every one a gated /__api call. (The ungated diagnostic lives at tb.log, so no
|
|
661
|
+
// built-in shadows a user export.)
|
|
658
662
|
server: new Proxy({}, {
|
|
659
|
-
get: (_, name) => name
|
|
660
|
-
? (...args) => serverLog(...args)
|
|
661
|
-
: (...args) => serverCall(name, args)
|
|
663
|
+
get: (_, name) => (...args) => serverCall(name, args)
|
|
662
664
|
}),
|
|
663
665
|
|
|
664
666
|
// Filesystem - local CLI extension
|
|
@@ -673,7 +675,7 @@ ${s}`}function Ff(t){let e=ur.filter(n=>t[n]).map(n=>zv(n,t[n]));return["---",`f
|
|
|
673
675
|
},
|
|
674
676
|
|
|
675
677
|
// Receive a value pushed from the terminal via \`typebulb send\` (data-in, the dual of the
|
|
676
|
-
// ungated tb.
|
|
678
|
+
// ungated tb.log). Returns an unsubscribe fn. Inert when embedded \u2014 no server, so no
|
|
677
679
|
// sender; the handler is registered but never fires (cf. tb.models returning []). A handler's
|
|
678
680
|
// non-undefined return (awaited) becomes the reply \`send --wait\` prints (TB-Interrogation.md).
|
|
679
681
|
onMessage: (handler) => {
|
|
@@ -794,7 +796,7 @@ ${KA}
|
|
|
794
796
|
|
|
795
797
|
`);return{text:n,reasoning:r||void 0}}parseProviderStreamChunk(e){return this.isAnthropicEvent(e)&&e.type==="content_block_delta"?e.delta.type==="text_delta"?{text:e.delta.text||""}:e.delta.type==="thinking_delta"?{reasoning:e.delta.thinking||""}:null:null}withTailMessagesCached(e){let n=Math.max(0,e.length-2);return e.map((r,s)=>s>=n?{role:r.role,content:[{type:"text",text:r.content,cache_control:{type:"ephemeral"}}]}:r)}isAnthropicResponse(e){return typeof e=="object"&&e!==null&&e.type==="message"&&Array.isArray(e.content)}isAnthropicEvent(e){return typeof e=="object"&&e!==null&&"type"in e}isModernModel(e){let n=e.match(/^claude-[a-z]+-(\d+)(?:-(\d+))?/i);if(!n)return!1;let r=+n[1],s=n[2]?+n[2]:0;return r>4||r===4&&s>=6}getMaxTokens(e){return this.isModernModel(e)?64e3:32e3}};var Nl=class extends Dn{constructor(){super(...arguments),this.providerName="Gemini",this.defaultBaseUrl="https://generativelanguage.googleapis.com",this.path="/v1beta/models",this.budgetMap={0:0,1:1024,2:8192,3:-1}}getPath(e,n){return`/v1beta/models/${e}:${n?"streamGenerateContent":"generateContent"}${n?"?alt=sse":""}`}buildHeaders(e){return{"Content-Type":"application/json","x-goog-api-key":e}}buildPayload(e,n,r,s){let{system:o,conversationMessages:i}=this.extractSystemMessages(e,`
|
|
796
798
|
`),l={contents:i.map(h=>({role:h.role==="assistant"?"model":"user",parts:[{text:h.content}]}))};r?.webSearch!==!1&&(l.tools=[{google_search:{}}]),o&&(l.systemInstruction={role:"system",parts:[{text:o}]});let c=r?.effort;if(c!==void 0){let h=this.geminiVersion(n),u=h!==null&&h>=3?{thinkingLevel:this.thinkingLevelFor(h,c)}:{thinkingBudget:this.budgetMap[c]};l.generationConfig={thinkingConfig:c===0?u:{includeThoughts:!0,...u}}}return l}parseError(e,n){if(!e)return{message:`HTTP ${n}`};try{let r=JSON.parse(e),s=Array.isArray(r)?r[0]?.error:r?.error;return s&&typeof s=="object"?{message:(s.message||`HTTP ${n}`).split(`
|
|
797
|
-
`)[0],type:s.status,code:s.code?.toString()}:r.message?{message:r.message}:{message:e}}catch{return{message:e}}}parseNonStreamingResponse(e){if(this.checkAndThrowError(e),this.checkGeminiError(e),!this.isGeminiResponse(e))return{text:"",status:"failed",error:"Invalid response format"};let n=this.extractParts(e).text||"",r=e.candidates?.[0]?.finishReason,s="complete";return r==="MAX_TOKENS"?s="interrupted":(r==="SAFETY"||r==="RECITATION")&&(s="failed"),{text:n,status:s}}parseProviderStreamChunk(e){if(this.checkGeminiError(e),!this.isGeminiResponse(e))return null;let n=this.extractParts(e);return n.text||n.reasoning?n:null}geminiVersion(e){let n=e.match(/^gemini-(\d+(?:\.\d+)?)/);return n?Number(n[1]):null}thinkingLevelFor(e,n){let r=["minimal","low","medium","high"][n];return r==="minimal"&&e<3.5?"low":r}isGeminiResponse(e){return typeof e=="object"&&e!==null&&"candidates"in e&&Array.isArray(e.candidates)}extractParts(e){let n=e.candidates?.[0]?.content?.parts;if(!n)return{};let r="",s="";for(let o of n)o.text&&(o.thought?s+=o.text:r+=o.text);return{text:r||void 0,reasoning:s||void 0}}checkGeminiError(e){if(this.isGeminiResponse(e)&&e.promptFeedback?.blockReason){let n=e.promptFeedback.blockReason;throw new lr(`Prompt blocked: ${n}`)}}};var Bl=class extends Fs{constructor(){super(...arguments),this.providerName="Ollama",this.defaultBaseUrl="http://localhost:11434",this.path="/chat/completions"}buildHeaders(e){let n={"Content-Type":"application/json",Accept:"application/json"};return e&&(n.Authorization=`Bearer ${e}`),n}buildPayload(e,n,r,s){return{model:n,messages:e,stream:s}}};var UA=new Bl,oQ=new Map([["openai",new Il],["openrouter",new _l],["anthropic",new Sl],["gemini",new Nl],["ollama",UA],["openai-compat",UA]]);function ci(t){let e=oQ.get(t);if(!e)throw new Error(`Unsupported protocol: ${t}`);return e}function iQ(t,e){return t.replace(/\/+$/,"")+(e.startsWith("/")?e:`/${e}`)}async function wf(t,e){let n=ci(t.protocol),r=n.getPath(t.model,e.stream),s=iQ(t.baseUrl,r),o=n.buildHeaders(t.apiKey,e.origin),i=n.buildPayload(e.messages,t.model,{effort:e.effort,webSearch:e.webSearch},e.stream);return e.modifyPayload?.(i),fetch(s,{method:"POST",headers:o,body:JSON.stringify(i),signal:e.signal})}var bf={anthropic:"ANTHROPIC_API_KEY",openai:"OPENAI_API_KEY",gemini:"GOOGLE_API_KEY",openrouter:"OPENROUTER_API_KEY"};function Rl(t){return(t.includes("://")?t:`http://${t}`).replace(/\/+$/,"")}var uQ="https://api.typebulb.com/api/models",pQ=1440*60*1e3,fQ=5e3,dQ=60*1e3,ui=null,Pl=null,pi=null;function hQ(){return pi}async function HA(){let[t,e]=await Promise.all([gQ(),yQ()]);return mQ([...TQ(e),...t],process.env.TB_AI_PROVIDER,process.env.TB_AI_MODEL)}function mQ(t,e,n){return!e||!n?t:t.map(r=>r.provider===e&&r.name===n?{...r,default:!0}:r)}async function JA(){let t;try{t=await fetch(uQ,{signal:AbortSignal.timeout(fQ)})}catch(e){return pi=`catalog fetch failed: ${e?.message??e}`,null}if(!t.ok)return pi=`catalog fetch failed: HTTP ${t.status}`,null;try{return await t.json()}catch(e){return pi=`catalog returned an unreadable body: ${e?.message??e}`,null}}async function yQ(){if(ui&&Date.now()-ui.fetchedAt<=pQ)return ui.models;let t=await JA()??await JA();return t?(ui={models:t,fetchedAt:Date.now()},pi=null,t):ui?.models??[]}async function gQ(){if(Pl&&Date.now()-Pl.fetchedAt<=dQ)return Pl.models;let t=Rl(process.env.OLLAMA_HOST??"http://localhost:11434"),e=[];try{let n=await fetch(new URL("/api/tags",t).toString());n.ok&&(e=((await n.json()).models??[]).map(s=>s.name).filter(s=>!!s).map(s=>({provider:"ollama",name:s,friendlyName:s,providerName:"Ollama"})))}catch{}return Pl={models:e,fetchedAt:Date.now()},e}function TQ(t){let e=new Set(Object.entries(bf).filter(([,n])=>!!process.env[n]).map(([n])=>n));return t.filter(n=>e.has(n.provider))}function GA(t,e){let n=t??process.env.TB_AI_PROVIDER,r=e??process.env.TB_AI_MODEL;if(!n)return"No provider specified. Set TB_AI_PROVIDER in your .env file or pass provider in the tb.ai() call.";if(!r)return"No model specified. Set TB_AI_MODEL in your .env file or pass model in the tb.ai() call.";let s;try{s=ci(n)}catch{return`Unknown provider '${n}'.`}if(n==="ollama")return{apiKey:"",baseUrl:`${Rl(process.env.OLLAMA_HOST??s.defaultBaseUrl)}/v1`,protocol:n,model:r,isFreeModel:!1};if(n==="openai-compat"){let a=process.env.TB_AI_BASE_URL;return a?{apiKey:process.env.TB_AI_API_KEY??"",baseUrl:a,protocol:n,model:r,isFreeModel:!1}:"No base URL for openai-compat. Set TB_AI_BASE_URL (the OpenAI-style base ending in /v1, e.g. http://localhost:1234/v1) in your .env file."}let o=bf[n];if(!o)return`Unknown provider '${n}'.`;let i=process.env[o];return i?{apiKey:i,baseUrl:s.defaultBaseUrl,protocol:n,model:r,isFreeModel:!1}:`No API key for '${n}'. Set ${o} in your .env file.`}function VA(t,e){let n=[...e.system?[{role:"system",content:e.system}]:[],...e.messages.map(s=>({role:s.role,content:s.content}))],r=e.effort;return wf(t,{messages:n,stream:!0,effort:r!=null&&r>=0&&r<=3?r:void 0,webSearch:e.webSearch??!0,signal:e.signal})}function xf(t,e){let n=wn.resolve(t),r=wn.join(wn.dirname(n),wn.basename(n).replace(/\.bulb\.md$/,""));return e?wn.join(r,"batches",e):r}var fi=(t,e)=>wn.join(xf(t,e),"assets");function YA(t){let[e,n,r,s]=t.split(wn.sep).slice(-4);if(!(e!=="typebulbs"||n!=="u"||!r||!s?.endsWith(".bulb.md")))return{userSlug:r,slug:s.slice(0,-8)}}function zA(t,e,n){if(e>8)return n;let r;try{r=WA(t,{withFileTypes:!0})}catch{return n}for(let s of r)if(s.isDirectory()){if(s.name.startsWith(".")||s.name==="node_modules")continue;zA(Ll(t,s.name),e+1,n)}else s.isFile()&&s.name.endsWith(".bulb.md")&&n.push(Ll(t,s.name));return n}function vQ(t){let e=Ll(xf(t),"batches"),n;try{n=WA(e,{withFileTypes:!0})}catch{return[]}return n.filter(r=>r.isDirectory()).map(r=>{let s=0;try{s=XA(Ll(e,r.name)).mtimeMs}catch{}return{name:r.name,mtime:s}}).sort((r,s)=>s.mtime-r.mtime).map(r=>r.name)}function wQ(t){return zA(t,0,[]).map(e=>{let n=0;try{n=XA(e).mtimeMs}catch{}let r,s=!1;try{let o=kQ(e,"utf8");r=da(o.slice(0,1024));let i=$n(o);s=!!i&&Pm(bo(i))}catch{}return{path:e,name:r??AQ(e).replace(/\.bulb\.md$/,""),mtime:n,serverOnly:s}})}import{mkdir as eJ,readFile as bQ,writeFile as tJ}from"fs/promises";import{join as ZA}from"path";var xQ=20100,ev=100,EQ=19;var oJ=ev-1;function CQ(){return ZA(ls(),"blocks")}function IQ(t){return ZA(CQ(),`${t}.json`)}async function _Q(t){try{let e=JSON.parse(await bQ(IQ(t),"utf8"));return e&&typeof e.cwd=="string"?{cwd:e.cwd,slots:e.slots??{},mirrors:e.mirrors??{}}:void 0}catch{return}}async function SQ(t){let e=_n(t);for(let n=0;n<EQ;n++){let r=await _Q(xQ+n*ev);if(r?.cwd===e)return s=>r.slots[_n(s)]?.usedAt??0}return()=>0}import{readFile as NQ}from"fs/promises";function tv(t){if(t.server.trim())return"server-side code (server.ts)";let e=t.code;if(/\btb\s*\.\s*fs\b/.test(e)||e.includes("/__fs"))return"the filesystem";if(/\btb\s*\.\s*ai\b/.test(e)||e.includes("/__ai")||t.infer.trim()||/\btb\s*\.\s*infer\b/.test(e))return"AI (your API keys)";if(/\btb\s*\.\s*server\s*\.\s
|
|
799
|
+
`)[0],type:s.status,code:s.code?.toString()}:r.message?{message:r.message}:{message:e}}catch{return{message:e}}}parseNonStreamingResponse(e){if(this.checkAndThrowError(e),this.checkGeminiError(e),!this.isGeminiResponse(e))return{text:"",status:"failed",error:"Invalid response format"};let n=this.extractParts(e).text||"",r=e.candidates?.[0]?.finishReason,s="complete";return r==="MAX_TOKENS"?s="interrupted":(r==="SAFETY"||r==="RECITATION")&&(s="failed"),{text:n,status:s}}parseProviderStreamChunk(e){if(this.checkGeminiError(e),!this.isGeminiResponse(e))return null;let n=this.extractParts(e);return n.text||n.reasoning?n:null}geminiVersion(e){let n=e.match(/^gemini-(\d+(?:\.\d+)?)/);return n?Number(n[1]):null}thinkingLevelFor(e,n){let r=["minimal","low","medium","high"][n];return r==="minimal"&&e<3.5?"low":r}isGeminiResponse(e){return typeof e=="object"&&e!==null&&"candidates"in e&&Array.isArray(e.candidates)}extractParts(e){let n=e.candidates?.[0]?.content?.parts;if(!n)return{};let r="",s="";for(let o of n)o.text&&(o.thought?s+=o.text:r+=o.text);return{text:r||void 0,reasoning:s||void 0}}checkGeminiError(e){if(this.isGeminiResponse(e)&&e.promptFeedback?.blockReason){let n=e.promptFeedback.blockReason;throw new lr(`Prompt blocked: ${n}`)}}};var Bl=class extends Fs{constructor(){super(...arguments),this.providerName="Ollama",this.defaultBaseUrl="http://localhost:11434",this.path="/chat/completions"}buildHeaders(e){let n={"Content-Type":"application/json",Accept:"application/json"};return e&&(n.Authorization=`Bearer ${e}`),n}buildPayload(e,n,r,s){return{model:n,messages:e,stream:s}}};var UA=new Bl,oQ=new Map([["openai",new Il],["openrouter",new _l],["anthropic",new Sl],["gemini",new Nl],["ollama",UA],["openai-compat",UA]]);function ci(t){let e=oQ.get(t);if(!e)throw new Error(`Unsupported protocol: ${t}`);return e}function iQ(t,e){return t.replace(/\/+$/,"")+(e.startsWith("/")?e:`/${e}`)}async function wf(t,e){let n=ci(t.protocol),r=n.getPath(t.model,e.stream),s=iQ(t.baseUrl,r),o=n.buildHeaders(t.apiKey,e.origin),i=n.buildPayload(e.messages,t.model,{effort:e.effort,webSearch:e.webSearch},e.stream);return e.modifyPayload?.(i),fetch(s,{method:"POST",headers:o,body:JSON.stringify(i),signal:e.signal})}var bf={anthropic:"ANTHROPIC_API_KEY",openai:"OPENAI_API_KEY",gemini:"GOOGLE_API_KEY",openrouter:"OPENROUTER_API_KEY"};function Rl(t){return(t.includes("://")?t:`http://${t}`).replace(/\/+$/,"")}var uQ="https://api.typebulb.com/api/models",pQ=1440*60*1e3,fQ=5e3,dQ=60*1e3,ui=null,Pl=null,pi=null;function hQ(){return pi}async function HA(){let[t,e]=await Promise.all([gQ(),yQ()]);return mQ([...TQ(e),...t],process.env.TB_AI_PROVIDER,process.env.TB_AI_MODEL)}function mQ(t,e,n){return!e||!n?t:t.map(r=>r.provider===e&&r.name===n?{...r,default:!0}:r)}async function JA(){let t;try{t=await fetch(uQ,{signal:AbortSignal.timeout(fQ)})}catch(e){return pi=`catalog fetch failed: ${e?.message??e}`,null}if(!t.ok)return pi=`catalog fetch failed: HTTP ${t.status}`,null;try{return await t.json()}catch(e){return pi=`catalog returned an unreadable body: ${e?.message??e}`,null}}async function yQ(){if(ui&&Date.now()-ui.fetchedAt<=pQ)return ui.models;let t=await JA()??await JA();return t?(ui={models:t,fetchedAt:Date.now()},pi=null,t):ui?.models??[]}async function gQ(){if(Pl&&Date.now()-Pl.fetchedAt<=dQ)return Pl.models;let t=Rl(process.env.OLLAMA_HOST??"http://localhost:11434"),e=[];try{let n=await fetch(new URL("/api/tags",t).toString());n.ok&&(e=((await n.json()).models??[]).map(s=>s.name).filter(s=>!!s).map(s=>({provider:"ollama",name:s,friendlyName:s,providerName:"Ollama"})))}catch{}return Pl={models:e,fetchedAt:Date.now()},e}function TQ(t){let e=new Set(Object.entries(bf).filter(([,n])=>!!process.env[n]).map(([n])=>n));return t.filter(n=>e.has(n.provider))}function GA(t,e){let n=t??process.env.TB_AI_PROVIDER,r=e??process.env.TB_AI_MODEL;if(!n)return"No provider specified. Set TB_AI_PROVIDER in your .env file or pass provider in the tb.ai() call.";if(!r)return"No model specified. Set TB_AI_MODEL in your .env file or pass model in the tb.ai() call.";let s;try{s=ci(n)}catch{return`Unknown provider '${n}'.`}if(n==="ollama")return{apiKey:"",baseUrl:`${Rl(process.env.OLLAMA_HOST??s.defaultBaseUrl)}/v1`,protocol:n,model:r,isFreeModel:!1};if(n==="openai-compat"){let a=process.env.TB_AI_BASE_URL;return a?{apiKey:process.env.TB_AI_API_KEY??"",baseUrl:a,protocol:n,model:r,isFreeModel:!1}:"No base URL for openai-compat. Set TB_AI_BASE_URL (the OpenAI-style base ending in /v1, e.g. http://localhost:1234/v1) in your .env file."}let o=bf[n];if(!o)return`Unknown provider '${n}'.`;let i=process.env[o];return i?{apiKey:i,baseUrl:s.defaultBaseUrl,protocol:n,model:r,isFreeModel:!1}:`No API key for '${n}'. Set ${o} in your .env file.`}function VA(t,e){let n=[...e.system?[{role:"system",content:e.system}]:[],...e.messages.map(s=>({role:s.role,content:s.content}))],r=e.effort;return wf(t,{messages:n,stream:!0,effort:r!=null&&r>=0&&r<=3?r:void 0,webSearch:e.webSearch??!0,signal:e.signal})}function xf(t,e){let n=wn.resolve(t),r=wn.join(wn.dirname(n),wn.basename(n).replace(/\.bulb\.md$/,""));return e?wn.join(r,"batches",e):r}var fi=(t,e)=>wn.join(xf(t,e),"assets");function YA(t){let[e,n,r,s]=t.split(wn.sep).slice(-4);if(!(e!=="typebulbs"||n!=="u"||!r||!s?.endsWith(".bulb.md")))return{userSlug:r,slug:s.slice(0,-8)}}function zA(t,e,n){if(e>8)return n;let r;try{r=WA(t,{withFileTypes:!0})}catch{return n}for(let s of r)if(s.isDirectory()){if(s.name.startsWith(".")||s.name==="node_modules")continue;zA(Ll(t,s.name),e+1,n)}else s.isFile()&&s.name.endsWith(".bulb.md")&&n.push(Ll(t,s.name));return n}function vQ(t){let e=Ll(xf(t),"batches"),n;try{n=WA(e,{withFileTypes:!0})}catch{return[]}return n.filter(r=>r.isDirectory()).map(r=>{let s=0;try{s=XA(Ll(e,r.name)).mtimeMs}catch{}return{name:r.name,mtime:s}}).sort((r,s)=>s.mtime-r.mtime).map(r=>r.name)}function wQ(t){return zA(t,0,[]).map(e=>{let n=0;try{n=XA(e).mtimeMs}catch{}let r,s=!1;try{let o=kQ(e,"utf8");r=da(o.slice(0,1024));let i=$n(o);s=!!i&&Pm(bo(i))}catch{}return{path:e,name:r??AQ(e).replace(/\.bulb\.md$/,""),mtime:n,serverOnly:s}})}import{mkdir as eJ,readFile as bQ,writeFile as tJ}from"fs/promises";import{join as ZA}from"path";var xQ=20100,ev=100,EQ=19;var oJ=ev-1;function CQ(){return ZA(ls(),"blocks")}function IQ(t){return ZA(CQ(),`${t}.json`)}async function _Q(t){try{let e=JSON.parse(await bQ(IQ(t),"utf8"));return e&&typeof e.cwd=="string"?{cwd:e.cwd,slots:e.slots??{},mirrors:e.mirrors??{}}:void 0}catch{return}}async function SQ(t){let e=_n(t);for(let n=0;n<EQ;n++){let r=await _Q(xQ+n*ev);if(r?.cwd===e)return s=>r.slots[_n(s)]?.usedAt??0}return()=>0}import{readFile as NQ}from"fs/promises";function tv(t){if(t.server.trim())return"server-side code (server.ts)";let e=t.code;if(/\btb\s*\.\s*fs\b/.test(e)||e.includes("/__fs"))return"the filesystem";if(/\btb\s*\.\s*ai\b/.test(e)||e.includes("/__ai")||t.infer.trim()||/\btb\s*\.\s*infer\b/.test(e))return"AI (your API keys)";if(/\btb\s*\.\s*server\s*\.\s*\w/.test(e)||e.includes("/__api"))return"server-side code (server.ts)"}async function BQ(t){try{let e=$n(await NQ(t,"utf-8"));return e?tv(bo(e)):void 0}catch{return}}function PQ(t){let e=$n(t);if(!e)return t;let n=e.files.get("code.tsx");if(!n)return t;let r=MT(n);if(!r.length)return t;let s=e.files.get("config.json"),o={};if(s)try{let c=JSON.parse(s);c&&typeof c=="object"&&(o=c)}catch{}let i=o.dependencies&&typeof o.dependencies=="object"?{...o.dependencies}:{},a=r.filter(c=>!(c in i));if(!a.length)return t;for(let c of a)i[c]="latest";o.dependencies=i;let l={name:e.frontmatter.name};for(let[c,h]of e.files){let u=Of(c);u&&(l[u]=h)}return l.config=JSON.stringify(o,null,2),Ff(l)}import{spawn as nv}from"child_process";import{basename as RQ}from"path";function LQ(){return process.env.TYPEBULB_EDITOR||"code"}var OQ=/^(code|code-insiders|codium|vscodium|cursor|windsurf)(\.cmd|\.exe)?$/i;function DQ(t,e,n){return n==null?[e]:OQ.test(RQ(t))?["-g",`${e}:${n}`]:[`+${n}`,e]}function QQ(t,e){let n=LQ();return{command:n,args:DQ(n,t,e)}}function qQ(t,e){let{command:n,args:r}=QQ(t,e),s=process.platform==="win32"?nv("cmd.exe",["/d","/s","/c",n,...r],{detached:!0,stdio:"ignore",windowsHide:!0}):nv(n,r,{detached:!0,stdio:"ignore"});s.on("error",o=>console.error("[typebulb] editor launch failed:",o?.message??o)),s.unref()}import{readFileSync as MQ,writeFileSync as $Q,mkdirSync as FQ}from"fs";import{join as jQ}from"path";function rv(){return jQ(ls(),"trust.json")}function Ef(){try{let t=JSON.parse(MQ(rv(),"utf8"));return new Set(Array.isArray(t)?t:[])}catch{return new Set}}function KQ(t){FQ(ls(),{recursive:!0}),$Q(rv(),JSON.stringify([...t]))}function UQ(t){return Ef().has(_n(t))}function JQ(t,e){let n=Ef(),r=_n(t);(e?n.has(r):!n.has(r))||(e?n.add(r):n.delete(r),KQ(n))}function HQ(){return[...Ef()]}import{readFileSync as bJ,existsSync as GQ}from"fs";import*as ts from"path";import{fileURLToPath as VQ}from"url";function sv(){let t=ts.dirname(VQ(import.meta.url));for(;!GQ(ts.join(t,"package.json"));){let e=ts.dirname(t);if(e===t)break;t=e}return t}function YQ(){return ts.join(sv(),"README.md")}function WQ(){return ts.join(sv(),"description.md")}import*as pn from"fs/promises";import*as Qn from"path";import{createHash as tq}from"crypto";import{readFileSync as XQ,existsSync as IJ}from"fs";import*as Ol from"path";function zQ(t){return[...t?[`.env.${t}.local`,`.env.${t}`]:[],".env.local",".env"]}function*ZQ(t){for(let e of t.split(`
|
|
798
800
|
`)){let n=e.trim();if(!n||n.startsWith("#"))continue;let r=n.indexOf("=");if(r===-1)continue;let s=n.slice(0,r).trim(),o=n.slice(r+1).trim();(o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'"))&&(o=o.slice(1,-1)),yield[s,o]}}function eq(t,e=process.cwd()){for(let n of zQ()){let r;try{r=XQ(Ol.resolve(e,n),"utf-8")}catch{continue}for(let[s,o]of ZQ(r))if(s===t)return o}return process.env[t]}var ov=(t,e)=>Qn.join("typebulbs","u",t,`${e}.bulb.md`),Cf=t=>`${t.origin}/u/${encodeURIComponent(t.userSlug)}/${encodeURIComponent(t.slug)}.md`,Dl=t=>t.split("/").map(encodeURIComponent).join("/"),di=t=>`${t.origin}/u/${encodeURIComponent(t.userSlug)}/${encodeURIComponent(t.slug)}/assets`;async function If(t,e,n){let r=[...t],s=async()=>{for(let o=r.shift();o!==void 0;o=r.shift())await n(o)};await Promise.all(Array.from({length:Math.min(e,t.length)},s))}var _f=t=>tq("md5").update(t).digest("hex");function js(t){try{return JSON.parse(t).error||t}catch{return t}}async function Sf(t,e){try{let n=await fetch(di(t),{headers:e.token?{Authorization:`Bearer ${e.token}`}:{},signal:e.timeoutMs?AbortSignal.timeout(e.timeoutMs):void 0});return n.ok?n.headers.get("content-type")?.includes("json")?{entries:await n.json()}:{error:"manifest response is not JSON \u2014 wrong origin?"}:{error:`manifest request failed (${n.status}): ${js(await n.text())}`}}catch(n){return{error:n instanceof Error?n.message:String(n)}}}function nq(t,e,n){if(/^https?:\/\//i.test(t)){let o=new URL(t),i=o.pathname.split("/").filter(Boolean).map(decodeURIComponent),a=i[0]==="api"&&i[1]==="scripts"?i.slice(2):i;if(a[0]!=="u"||!a[1]||!a[2])throw new Error(`Not a bulb URL: ${t}
|
|
799
801
|
Expected <origin>/u/<user>/<slug>`);let l=a[1],c=a[2].replace(/(\.bulb)?\.md$/,"");return{origin:o.origin,userSlug:l,slug:c,dest:Qn.join(e,ov(l,c))}}let r=Qn.resolve(e,t),s=YA(r);if(!s)throw new Error(`Not a conventional bulb path: ${t}
|
|
800
802
|
A pull target must be a bulb URL, or a local file at typebulbs/u/<user>/<slug>.bulb.md \u2014 the path is its remote identity.`);return{origin:n,...s,dest:r}}async function rq(t,e){let n=await Sf(t,e);if("error"in n||!n.entries.length)return{kind:"none"};let r=n.entries,s=fi(t.dest),o=[],i=[],a=0;for(let l of r){let c=await pn.readFile(Qn.join(s,...l.path.split("/"))).catch(()=>{});c===void 0?o.push(l.path):_f(c)===l.md5?a++:e.force?o.push(l.path):i.push(l.path)}return i.length?{kind:"conflict",paths:i}:{kind:"plan",toGet:o,unchanged:a}}async function sq(t,e,n){let r=fi(t.dest),s=[],o=0;return await If(e.toGet,4,async i=>{try{let a=await fetch(`${di(t)}/${Dl(i)}`,{headers:n.token?{Authorization:`Bearer ${n.token}`}:{},signal:AbortSignal.timeout(n.timeoutMs??3e4)});if(!a.ok){s.push(`${i}: HTTP ${a.status}`);return}let l=Qn.join(r,...i.split("/"));await pn.mkdir(Qn.dirname(l),{recursive:!0}),await pn.writeFile(l,new Uint8Array(await a.arrayBuffer())),o++}catch(a){s.push(`${i}: ${a instanceof Error?a.message:String(a)}`)}}),{downloaded:o,unchanged:e.unchanged,errors:s}}async function oq(t,e={}){let n=await fetch(Cf(t),{headers:e.token?{Authorization:`Bearer ${e.token}`}:{},signal:e.timeoutMs?AbortSignal.timeout(e.timeoutMs):void 0});if(!n.ok)return{kind:"http-error",status:n.status};let r=n.headers.get("content-type");if(!r?.includes("text/markdown"))return{kind:"not-markdown",contentType:r};let s=await n.text(),o;try{o=await pn.readFile(t.dest,"utf-8")}catch{}let i=o!==void 0&&iq(o,s);if(o!==void 0&&!i&&!e.force){let{mtime:c}=await pn.stat(t.dest);return{kind:"conflict",dest:t.dest,localMtime:c,remoteLastModified:n.headers.get("last-modified")}}let a=await rq(t,e);if(a.kind==="conflict")return{kind:"asset-conflict",dest:t.dest,paths:a.paths};if(!i){await pn.mkdir(Qn.dirname(t.dest),{recursive:!0}),await pn.writeFile(t.dest,s);let c=new Date(n.headers.get("last-modified")??"");isNaN(c.getTime())||await pn.utimes(t.dest,new Date,c)}let l=a.kind==="plan"?await sq(t,a,e):void 0;return i?{kind:"up-to-date",dest:t.dest,assets:l}:{kind:"written",dest:t.dest,created:o===void 0,assets:l}}function iq(t,e){let n=o=>o.replace(/\r\n/g,`
|
package/package.json
CHANGED