@decido/kernel-bridge 4.0.3 → 4.0.4

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.
Files changed (3) hide show
  1. package/dist/index.js +25 -2552
  2. package/dist/index.mjs +25 -2477
  3. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -1,1216 +1,17 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
1
+ "use strict";var Ye=Object.create;var H=Object.defineProperty;var Qe=Object.getOwnPropertyDescriptor;var Ze=Object.getOwnPropertyNames;var et=Object.getPrototypeOf,tt=Object.prototype.hasOwnProperty;var nt=(n,e)=>{for(var t in e)H(n,t,{get:e[t],enumerable:!0})},he=(n,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Ze(e))!tt.call(n,r)&&r!==t&&H(n,r,{get:()=>e[r],enumerable:!(s=Qe(e,r))||s.enumerable});return n};var M=(n,e,t)=>(t=n!=null?Ye(et(n)):{},he(e||!n||!n.__esModule?H(t,"default",{value:n,enumerable:!0}):t,n)),st=n=>he(H({},"__esModule",{value:!0}),n);var zt={};nt(zt,{AnthropicProvider:()=>L,GeminiProvider:()=>_,OllamaProvider:()=>T,OpenAIProvider:()=>R,PeerNetworkPanel:()=>Xe,StateRehydrationManager:()=>qt,TokenWalletPanel:()=>Je,chat:()=>ie,chatStream:()=>ce,clearConversationHistory:()=>_e,decryptEvent:()=>ge,embeddingService:()=>Ke,getAllProviders:()=>O,getProviderStatuses:()=>te,inferenceRouter:()=>K,initProviders:()=>Z,isOllamaAvailable:()=>Le,kernel:()=>F,loadProviderKeys:()=>D,mlxBenchmarkModel:()=>$e,mlxCompareModels:()=>Ne,mlxListModels:()=>Ie,mlxPullModel:()=>Ee,mlxRunInference:()=>B,ollamaChat:()=>ie,ollamaChatStream:()=>ce,ollamaListModels:()=>Re,parseToolCalls:()=>Ce,peerMesh:()=>b,processLocalMessage:()=>Fe,routeChat:()=>A,routeInference:()=>ne,saveProviderKeys:()=>Q,setProviderApiKey:()=>ee,startLoRATraining:()=>Oe,stripToolCalls:()=>Te,tokenWallet:()=>P,usePeerMesh:()=>q,useTokenWallet:()=>W});module.exports=st(zt);var ke=require("socket.io-client");var z=new Map;function E(n){z.set(n.id,n),console.log(`\u{1F50C} [LLM] Provider registered: ${n.name} (${n.id})`)}function $(n){return z.get(n)}function O(){return Array.from(z.values())}var G="http://localhost:11434",rt="qwen2:latest",T=class{id="ollama";name="Ollama (Local)";requiresApiKey=!1;defaultModel=rt;async listModels(){try{let e=await fetch(`${G}/api/tags`);return e.ok?((await e.json()).models||[]).map(s=>s.name):[]}catch{return[]}}async checkStatus(){try{return(await fetch(`${G}/api/tags`,{signal:AbortSignal.timeout(2e3)})).ok?"available":"error"}catch{return"unavailable"}}async chat(e,t){let s=t?.model||this.defaultModel,r=Date.now(),o=e.map(l=>({role:l.role,content:l.content})),a=await fetch(`${G}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:s,messages:o,stream:!1,options:{temperature:t?.temperature??.7,num_predict:t?.maxTokens??512}}),signal:t?.signal??AbortSignal.timeout(3e4)});if(!a.ok){let l=await a.text();throw new Error(`Ollama API error ${a.status}: ${l}`)}let i=await a.json(),d=Date.now()-r;return{text:i.message?.content||"(sin respuesta)",model:s,latencyMs:d,tokensUsed:i.eval_count}}};var fe="https://generativelanguage.googleapis.com/v1beta",ot="gemini-flash-lite-latest",at=["gemini-flash-lite-latest","gemini-flash-lite-latest-lite","gemini-1.5-pro","gemini-flash-latest"],_=class{id="gemini";name="Google Gemini";requiresApiKey=!0;defaultModel=ot;apiKey=null;setApiKey(e){this.apiKey=e}async listModels(){return at}async checkStatus(){if(!this.apiKey)return"unconfigured";try{return(await fetch(`${fe}/models?key=${this.apiKey}`,{signal:AbortSignal.timeout(5e3)})).ok?"available":"error"}catch{return"unavailable"}}async chat(e,t){if(!this.apiKey)throw new Error("Gemini API key not configured. Set it in Settings \u2192 Providers.");let s=t?.model||this.defaultModel,r=Date.now(),o=e.find(y=>y.role==="system"),d={contents:e.filter(y=>y.role!=="system").map(y=>({role:y.role==="assistant"?"model":"user",parts:[{text:y.content}]})),generationConfig:{temperature:t?.temperature??.7,maxOutputTokens:t?.maxTokens??2048}};o&&(d.systemInstruction={parts:[{text:o.content}]});let l=`${fe}/models/${s}:generateContent?key=${this.apiKey}`,h=await fetch(l,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(d),signal:t?.signal??AbortSignal.timeout(6e4)});if(!h.ok){let y=await h.text();throw new Error(`Gemini API error ${h.status}: ${y}`)}let m=await h.json(),g=Date.now()-r,p=m.candidates?.[0]?.content?.parts?.map(y=>y.text||"").join("")||"(sin respuesta)",f=(m.usageMetadata?.promptTokenCount||0)+(m.usageMetadata?.candidatesTokenCount||0);return{text:p,model:s,latencyMs:g,tokensUsed:f}}};var J="https://api.anthropic.com/v1",it="claude-sonnet-4-20250514",X="2023-06-01",ct=["claude-sonnet-4-20250514","claude-3-5-sonnet-20241022","claude-3-5-haiku-20241022","claude-3-opus-20240229"],L=class{id="anthropic";name="Anthropic Claude";requiresApiKey=!0;defaultModel=it;apiKey=null;setApiKey(e){this.apiKey=e}async listModels(){return ct}async checkStatus(){if(!this.apiKey)return"unconfigured";try{let e=await fetch(`${J}/messages`,{method:"POST",headers:{"x-api-key":this.apiKey,"anthropic-version":X,"content-type":"application/json"},body:JSON.stringify({model:this.defaultModel,max_tokens:1,messages:[{role:"user",content:"ping"}]}),signal:AbortSignal.timeout(5e3)});return e.ok||e.status===429?"available":"error"}catch{return"unavailable"}}async chat(e,t){if(!this.apiKey)throw new Error("Anthropic API key not configured. Set it in Settings \u2192 Providers.");let s=t?.model||this.defaultModel,r=Date.now(),o=e.find(p=>p.role==="system"),a=e.filter(p=>p.role!=="system").map(p=>({role:p.role,content:p.content})),i=this._sanitizeMessages(a),d={model:s,max_tokens:t?.maxTokens??2048,messages:i};t?.temperature!==void 0&&(d.temperature=t.temperature),o&&(d.system=o.content);let l=await fetch(`${J}/messages`,{method:"POST",headers:{"x-api-key":this.apiKey,"anthropic-version":X,"content-type":"application/json"},body:JSON.stringify(d),signal:t?.signal??AbortSignal.timeout(6e4)});if(!l.ok){let p=await l.text();throw new Error(`Anthropic API error ${l.status}: ${p}`)}let h=await l.json(),m=Date.now()-r,g=(h.content||[]).filter(p=>p.type==="text").map(p=>p.text).join("")||"(sin respuesta)",v=(h.usage?.input_tokens||0)+(h.usage?.output_tokens||0);return{text:g,model:s,latencyMs:m,tokensUsed:v}}async*chatStream(e,t){if(!this.apiKey)throw new Error("Anthropic API key not configured.");let s=t?.model||this.defaultModel,r=e.find(g=>g.role==="system"),o=e.filter(g=>g.role!=="system").map(g=>({role:g.role,content:g.content})),a=this._sanitizeMessages(o),i={model:s,max_tokens:t?.maxTokens??2048,messages:a,stream:!0};t?.temperature!==void 0&&(i.temperature=t.temperature),r&&(i.system=r.content);let d=await fetch(`${J}/messages`,{method:"POST",headers:{"x-api-key":this.apiKey,"anthropic-version":X,"content-type":"application/json"},body:JSON.stringify(i),signal:t?.signal??AbortSignal.timeout(12e4)});if(!d.ok){let g=await d.text();throw new Error(`Anthropic stream error ${d.status}: ${g}`)}let l=d.body?.getReader();if(!l)return;let h=new TextDecoder,m="";for(;;){let{done:g,value:v}=await l.read();if(g)break;m+=h.decode(v,{stream:!0});let p=m.split(`
2
+ `);m=p.pop()||"";for(let f of p){if(!f.startsWith("data: "))continue;let y=f.slice(6).trim();if(y==="[DONE]")return;try{let N=JSON.parse(y);N.type==="content_block_delta"&&N.delta?.text&&(yield N.delta.text)}catch{}}}}_sanitizeMessages(e){if(e.length===0)return[{role:"user",content:"..."}];let t=[],s="";for(let r of e)r.role===s?t[t.length-1].content+=`
3
+ `+r.content:(t.push({...r}),s=r.role);return t[0]?.role!=="user"&&t.unshift({role:"user",content:"..."}),t}};var lt="https://api.openai.com/v1",dt="gpt-4o",U=["gpt-4o","gpt-4o-mini","gpt-4-turbo","gpt-4","gpt-3.5-turbo","o3-mini"],R=class{id="openai";name="OpenAI";requiresApiKey=!0;defaultModel=dt;apiKey=null;baseUrl=lt;setApiKey(e){this.apiKey=e}setBaseUrl(e){this.baseUrl=e.replace(/\/$/,"")}async listModels(){if(!this.apiKey)return U;try{let e=await fetch(`${this.baseUrl}/models`,{headers:{Authorization:`Bearer ${this.apiKey}`},signal:AbortSignal.timeout(5e3)});if(!e.ok)return U;let s=((await e.json()).data||[]).map(r=>r.id).filter(r=>r.startsWith("gpt-")||r.startsWith("o"));return s.length>0?s:U}catch{return U}}async checkStatus(){if(!this.apiKey)return"unconfigured";try{return(await fetch(`${this.baseUrl}/models`,{headers:{Authorization:`Bearer ${this.apiKey}`},signal:AbortSignal.timeout(5e3)})).ok?"available":"error"}catch{return"unavailable"}}async chat(e,t){if(!this.apiKey)throw new Error("OpenAI API key not configured. Set it in Settings \u2192 Providers.");let s=t?.model||this.defaultModel,r=Date.now(),o={model:s,messages:e.map(m=>({role:m.role,content:m.content})),temperature:t?.temperature??.7,max_tokens:t?.maxTokens??2048},a=await fetch(`${this.baseUrl}/chat/completions`,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(o),signal:t?.signal??AbortSignal.timeout(6e4)});if(!a.ok){let m=await a.text();throw new Error(`OpenAI API error ${a.status}: ${m}`)}let i=await a.json(),d=Date.now()-r,l=i.choices?.[0]?.message?.content||"(sin respuesta)",h=(i.usage?.prompt_tokens||0)+(i.usage?.completion_tokens||0);return{text:l,model:s,latencyMs:d,tokensUsed:h}}async*chatStream(e,t){if(!this.apiKey)throw new Error("OpenAI API key not configured.");let r={model:t?.model||this.defaultModel,messages:e.map(l=>({role:l.role,content:l.content})),temperature:t?.temperature??.7,max_tokens:t?.maxTokens??2048,stream:!0},o=await fetch(`${this.baseUrl}/chat/completions`,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(r),signal:t?.signal??AbortSignal.timeout(12e4)});if(!o.ok){let l=await o.text();throw new Error(`OpenAI stream error ${o.status}: ${l}`)}let a=o.body?.getReader();if(!a)return;let i=new TextDecoder,d="";for(;;){let{done:l,value:h}=await a.read();if(l)break;d+=i.decode(h,{stream:!0});let m=d.split(`
4
+ `);d=m.pop()||"";for(let g of m){if(!g.startsWith("data: "))continue;let v=g.slice(6).trim();if(v==="[DONE]")return;try{let f=JSON.parse(v).choices?.[0]?.delta?.content;f&&(yield f)}catch{}}}}};var ye={"qwen2:latest":{input:0,output:0},"llama3:latest":{input:0,output:0},"mistral:latest":{input:0,output:0},"codellama:latest":{input:0,output:0},"gemini-flash-lite-latest":{input:.1,output:.4},"gemini-flash-lite-latest-lite":{input:.075,output:.3},"gemini-1.5-pro":{input:1.25,output:5},"gemini-flash-latest":{input:.075,output:.3},"claude-sonnet-4-20250514":{input:3,output:15},"claude-3-5-sonnet-20241022":{input:3,output:15},"claude-3-5-haiku-20241022":{input:.8,output:4},"claude-3-opus-20240229":{input:15,output:75},"gpt-4o":{input:2.5,output:10},"gpt-4o-mini":{input:.15,output:.6},"gpt-4-turbo":{input:10,output:30},"gpt-4":{input:30,output:60},"gpt-3.5-turbo":{input:.5,output:1.5},"o3-mini":{input:1.1,output:4.4}},ut={input:1,output:3},ve="decido-token-wallet",xe=500,V=class{history=[];sessionStart=Date.now();listeners=new Set;constructor(){this._loadFromStorage()}record(e){let t=e.tokensUsed??(e.inputTokens??0)+(e.outputTokens??0),s=e.inputTokens??Math.round(t*.4),r=e.outputTokens??t-s,o=this._getPricing(e.model,e.provider),a=s/1e6*o.input+r/1e6*o.output,i={provider:e.provider,model:e.model,inputTokens:s,outputTokens:r,totalTokens:t,estimatedCostUsd:a,timestamp:Date.now()};this.history.push(i),this.history.length>xe&&(this.history=this.history.slice(-xe)),this._saveToStorage(),this._emit(),console.log(`\u{1F4B0} [Wallet] ${e.provider}/${e.model}: ${t} tokens \u2248 $${a.toFixed(6)}`)}getSummary(){let e={},t=0,s=0;for(let r of this.history)t+=r.totalTokens,s+=r.estimatedCostUsd,e[r.provider]||(e[r.provider]={tokens:0,cost:0,calls:0}),e[r.provider].tokens+=r.totalTokens,e[r.provider].cost+=r.estimatedCostUsd,e[r.provider].calls+=1;return{totalTokens:t,totalCostUsd:s,totalCalls:this.history.length,byProvider:e,sessionStart:this.sessionStart}}getHistory(){return[...this.history]}getRecentHistory(e){return this.history.slice(-e)}clearHistory(){this.history=[],this.sessionStart=Date.now(),this._saveToStorage(),this._emit()}subscribe(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}_getPricing(e,t){return ye[e]?ye[e]:t==="ollama"||t==="mlx"?{input:0,output:0}:ut}_saveToStorage(){try{let e=JSON.stringify({history:this.history,sessionStart:this.sessionStart});localStorage.setItem(ve,e)}catch{}}_loadFromStorage(){try{let e=localStorage.getItem(ve);if(e){let t=JSON.parse(e);this.history=t.history||[],this.sessionStart=t.sessionStart||Date.now()}}catch{}}_emit(){let e=this.getSummary();for(let t of this.listeners)try{t(e)}catch{}}},P=new V;var mt={preferredBackend:"auto",maxLatencyMs:3e4,enableFallback:!0,temperature:.7,maxTokens:512},be=!1;function Y(){be||(be=!0,E(new T),E(new _),E(new L),E(new R),console.log("\u{1F50C} [InferenceRouter] 4 providers initialized (Ollama, Gemini, Anthropic, OpenAI)"))}var k=null;async function Pe(){try{return(await import("@tauri-apps/api/core")).invoke}catch{return null}}async function D(){if(k)return k;try{let n=await Pe();if(n){let e=await n("load_provider_keys");if(e)return k=JSON.parse(e),k}}catch(n){console.warn("[InferenceRouter] Failed to load provider keys:",n)}try{let n=localStorage.getItem("macia-provider-keys");if(n)return k=JSON.parse(n),k}catch{}return k={},k}async function Q(n){k=n;try{let e=await Pe();if(e){await e("save_provider_keys",{json:JSON.stringify(n,null,2)});return}}catch(e){console.warn("[InferenceRouter] Failed to save provider keys via Tauri:",e)}try{localStorage.setItem("macia-provider-keys",JSON.stringify(n))}catch{}}async function Z(){Y();let n=await D(),e={gemini:n.gemini,anthropic:n.anthropic,openai:n.openai};for(let[t,s]of Object.entries(e))s&&($(t)?.setApiKey?.(s),console.log(`\u{1F511} [InferenceRouter] ${t} API key loaded`))}async function ee(n,e){let t=$(n);t?.setApiKey&&t.setApiKey(e);let s=await D();s[n]=e,await Q(s)}async function te(){Y();let n={};for(let e of O())n[e.id]=await e.checkStatus();return n}function pt(n){let e=n.toLowerCase();return["ejecuta","revisa","escanea","limpia","optimiza","monitorea","diagnostica","analiza el sistema","procesos","disco","memoria","red","seguridad","git","commit","deploy","backup"].some(a=>e.includes(a))?"tool-calling":["codigo","funcion","clase","error","debug","refactor","typescript","python","rust"].some(a=>e.includes(a))?"code":["analiza","compara","benchmark","rendimiento","metricas","estadisticas"].some(a=>e.includes(a))?"analysis":["genera","crea","escribe","inventa","imagina","dise\xF1a"].some(a=>e.includes(a))?"creative":"chat"}async function A(n,e={}){Y();let t={...mt,...e},s=gt(t.preferredBackend);for(let r of s){let o=$(r);if(!o)continue;let a=await o.checkStatus();if(a!=="available"){if(!t.enableFallback)throw new Error(`Provider ${o.name} is ${a}`);continue}try{let i=await o.chat(n,{model:void 0,temperature:t.temperature,maxTokens:t.maxTokens});return P.record({provider:r,model:i.model,tokensUsed:i.tokensUsed}),{...i,backend:r}}catch(i){if(console.warn(`[InferenceRouter] ${o.name} failed:`,i),!t.enableFallback)throw i}}throw new Error("No hay backends de IA disponibles. Inicia Ollama o configura una API key de Gemini.")}function gt(n){switch(n){case"ollama":return["ollama","gemini","anthropic","openai"];case"gemini":return["gemini","ollama","anthropic","openai"];case"anthropic":return["anthropic","gemini","ollama","openai"];case"openai":return["openai","gemini","ollama","anthropic"];default:return["ollama","gemini","anthropic","openai"]}}async function ne(n,e={}){let t=[{role:"user",content:n}];try{let s=await A(t,e);return{text:s.text,backend:s.backend,model:s.model,latencyMs:s.latencyMs,tokensPerSecond:s.tokensUsed?s.tokensUsed/(s.latencyMs/1e3):0,toolsInvoked:[]}}catch{return{text:"No hay backends disponibles. Inicia Ollama o configura un modelo.",backend:"auto",model:"none",latencyMs:0,tokensPerSecond:0,toolsInvoked:[]}}}var K={route:ne,routeChat:A,classifyTask:pt,initProviders:Z,getProviderStatuses:te,setProviderApiKey:ee,loadProviderKeys:D,getAllProviders:O,getProvider:$,tokenWallet:P};var we=require("@decido/sdk");async function ht(){return import("@tauri-apps/api/core")}async function ft(){return import("@tauri-apps/api/event")}var se=class{name="Anillo 0/1 (Tauri IPC)";isConnectedToCloud=!1;unlistenFns=[];connectToSwarm(e,t,s){console.log(`[Kernel/Tauri] Conexi\xF3n nativa delegada a Core-Rust para tenant: ${e}`)}async execute(e,t,s){let{invoke:r}=await ht();return r(t,{tenantId:e,...s})}async notify(e,t){try{let{sendNotification:s}=await import("@tauri-apps/plugin-notification");await s({title:e,body:t})}catch{console.log("[Kernel/Tauri] Notification plugin not available.")}}async vibrate(e){try{let{vibrate:t}=await import("@tauri-apps/plugin-haptics");await t(e)}catch{console.log("[Kernel/Tauri] Haptics not available on this platform.")}}async getEventHistory(e,t){return this.execute(e,"get_stream_history",{limit:t})}listenEvents(e){let t=null;return ft().then(({listen:s})=>{s("orchestrator-event",r=>{e({type:"ipc_in",channel:"orchestrator-event",payload:r.payload})}).then(r=>{t=r,this.unlistenFns.push(r)})}),()=>{t&&(t(),this.unlistenFns=this.unlistenFns.filter(s=>s!==t))}}},re=class{name="Sat\xE9lite Web (WebSocket)";isConnectedToCloud=!1;sockets={};eventListeners=new Set;connectToSwarm(e,t,s){if(this.sockets[e])return;let r=(0,ke.io)(t,{auth:s?{token:s}:void 0});r.on("connect",()=>{this.isConnectedToCloud=!0,console.log(`\u2705 [Kernel/WS] Sat\xE9lite conectado al Enjambre del Tenant: ${e}`)}),r.on("orchestrator-event",o=>{this.eventListeners.forEach(a=>a({type:"ipc_in",channel:"orchestrator-event",payload:{tenantId:e,...o}}))}),r.on("rpc_broadcast",o=>{console.log(`[Kernel] \u{1F310} Broadcast Recibido [${e}]:`,o),this.eventListeners.forEach(a=>a({type:"ipc_in",channel:"rpc_broadcast",payload:{tenantId:e,...o}}))}),this.sockets[e]=r}async execute(e,t,s){let r=this.sockets[e];return r?new Promise((o,a)=>{r.emit("rpc_call",{cmd:t,args:s},i=>{i.error?a(new Error(i.error)):o(i.data)})}):(console.warn(`[Kernel Mock] Executing ${t} for tenant ${e}`,s),{})}async notify(e,t){console.log(`[Notification Mock] ${e}: ${t}`)}async vibrate(e){typeof navigator<"u"&&navigator.vibrate&&navigator.vibrate(e==="heavy"?200:50)}async getEventHistory(e,t){return console.warn("[Kernel Mock] Cannot fetch stream history in Web Sandbox."),[]}listenEvents(e){return this.eventListeners.add(e),()=>this.eventListeners.delete(e)}},oe=class{logs=[];logListeners=new Set;universalListeners=new Set;transport;rateLimits=new Map;checkRateLimit(e){let t=Date.now(),s=1e3,r=50,o=this.rateLimits.get(e);return(!o||o.resetAt<t)&&(o={calls:0,resetAt:t+s},this.rateLimits.set(e,o)),o.calls++,o.calls>r?(console.warn(`[Kernel Throttle] \u{1F6A8} Command ${e} blocked (Rate limit: >50 calls/sec). Possible plugin infinite loop.`),!1):!0}constructor(){let e=typeof window<"u"&&"__TAURI_INTERNALS__"in window;this.transport=e?new se:new re,console.log(`[Kernel] Entorno Detectado. Operando en Modo ${this.transport.name}.`),this.transport.listenEvents(t=>{t.type==="ipc_in"&&(this.addLog({type:"ipc_in",channel:t.channel,payload:t.payload}),this.notifyUniversalListeners(t.payload.payload||t.payload))})}get isConnectedToCloud(){return this.transport.isConnectedToCloud}connectToSwarm(e,t,s){this.transport.connectToSwarm(e,t,s)}notifyUniversalListeners(e){let t=(0,we.createSafeEvent)(e);if(!t.success){console.error("[Kernel] \u{1F6A8} Inbound Mesh Event Failed Contract Validation:",t.error.format()),this.universalListeners.forEach(s=>s({type:"system_alert",source:"kernel-bridge",payload:{level:"error",title:"Contract Violation",message:"A received event did not match the strict schema. Ignored."}}));return}this.universalListeners.forEach(s=>s(t.data))}injectEvent(e){this.notifyUniversalListeners(e)}addLog(e){let t={id:typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():Math.random().toString(36).substring(2,9),timestamp:Date.now(),...e};this.logs=[t,...this.logs].slice(0,500),this.logListeners.forEach(s=>s(this.logs))}onLogsChange(e){return this.logListeners.add(e),e(this.logs),()=>this.logListeners.delete(e)}async execute(e,t,s){let r="system",o=e,a=t;if(typeof t=="string"&&(r=e,o=t,a=s),!this.checkRateLimit(o))throw new Error(`[Kernel Throttle] Request dropped for ${o} (Rate limit exceeded)`);let i=performance.now();try{let d=await this.transport.execute(r,o,a);return this.addLog({type:"ipc_out",channel:o,payload:{tenantId:r,...a},duration:performance.now()-i}),d}catch(d){throw this.addLog({type:"error",channel:o,payload:{tenantId:r,args:a,error:d},duration:performance.now()-i}),d}}async notify(e,t){return this.transport.notify(e,t)}async vibrate(e="medium"){return this.transport.vibrate(e)}async captureScreen(e="system"){return this.execute(e,"capture_screen")}async getMachineId(e="system"){return this.execute(e,"get_machine_id")}async initiateSingularity(e="system"){return this.execute(e,"initiate_singularity")}async broadcastMessage(e,t){return this.execute(e,"broadcast_message",{message:t})}async fetchMemories(e="system",t,s=20){return this.execute(e,"fetch_memories",{query:t,limit:s})}async storeMemory(e="system",t,s={}){return this.execute(e,"store_memory",{text:t,metadata:s})}async deleteMemory(e="system",t){return this.execute(e,"delete_memory",{id:t})}async reasonWithMLX(e,t,s){let r=t;s&&(r+=`
29
5
 
30
- // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
33
- AnthropicProvider: () => AnthropicProvider,
34
- GeminiProvider: () => GeminiProvider,
35
- OllamaProvider: () => OllamaProvider,
36
- OpenAIProvider: () => OpenAIProvider,
37
- PeerNetworkPanel: () => PeerNetworkPanel,
38
- StateRehydrationManager: () => StateRehydrationManager,
39
- TokenWalletPanel: () => TokenWalletPanel,
40
- chat: () => chat,
41
- chatStream: () => chatStream,
42
- clearConversationHistory: () => clearConversationHistory,
43
- decryptEvent: () => decryptEvent,
44
- embeddingService: () => embeddingService,
45
- getAllProviders: () => getAllProviders,
46
- getProviderStatuses: () => getProviderStatuses,
47
- inferenceRouter: () => inferenceRouter,
48
- initProviders: () => initProviders,
49
- isOllamaAvailable: () => isOllamaAvailable,
50
- kernel: () => kernel,
51
- loadProviderKeys: () => loadProviderKeys,
52
- mlxBenchmarkModel: () => benchmarkModel,
53
- mlxCompareModels: () => compareModels,
54
- mlxListModels: () => listAvailableModels,
55
- mlxPullModel: () => pullModel,
56
- mlxRunInference: () => runInference,
57
- ollamaChat: () => chat,
58
- ollamaChatStream: () => chatStream,
59
- ollamaListModels: () => listModels,
60
- parseToolCalls: () => parseToolCalls,
61
- peerMesh: () => peerMesh,
62
- processLocalMessage: () => processLocalMessage,
63
- routeChat: () => routeChat,
64
- routeInference: () => routeInference,
65
- saveProviderKeys: () => saveProviderKeys,
66
- setProviderApiKey: () => setProviderApiKey,
67
- startLoRATraining: () => startLoRATraining,
68
- stripToolCalls: () => stripToolCalls,
69
- tokenWallet: () => tokenWallet,
70
- usePeerMesh: () => usePeerMesh,
71
- useTokenWallet: () => useTokenWallet
72
- });
73
- module.exports = __toCommonJS(index_exports);
74
-
75
- // src/kernel.ts
76
- var import_socket = require("socket.io-client");
77
-
78
- // src/ai/services/providers/LLMProvider.ts
79
- var providers = /* @__PURE__ */ new Map();
80
- function registerProvider(provider) {
81
- providers.set(provider.id, provider);
82
- console.log(`\u{1F50C} [LLM] Provider registered: ${provider.name} (${provider.id})`);
83
- }
84
- function getProvider(id) {
85
- return providers.get(id);
86
- }
87
- function getAllProviders() {
88
- return Array.from(providers.values());
89
- }
90
-
91
- // src/ai/services/providers/OllamaProvider.ts
92
- var OLLAMA_BASE_URL = "http://localhost:11434";
93
- var DEFAULT_MODEL = "qwen2:latest";
94
- var OllamaProvider = class {
95
- id = "ollama";
96
- name = "Ollama (Local)";
97
- requiresApiKey = false;
98
- defaultModel = DEFAULT_MODEL;
99
- async listModels() {
100
- try {
101
- const res = await fetch(`${OLLAMA_BASE_URL}/api/tags`);
102
- if (!res.ok) return [];
103
- const data = await res.json();
104
- return (data.models || []).map((m) => m.name);
105
- } catch {
106
- return [];
107
- }
108
- }
109
- async checkStatus() {
110
- try {
111
- const res = await fetch(`${OLLAMA_BASE_URL}/api/tags`, {
112
- signal: AbortSignal.timeout(2e3)
113
- });
114
- return res.ok ? "available" : "error";
115
- } catch {
116
- return "unavailable";
117
- }
118
- }
119
- async chat(messages, options) {
120
- const model = options?.model || this.defaultModel;
121
- const start = Date.now();
122
- const ollamaMessages = messages.map((m) => ({
123
- role: m.role,
124
- content: m.content
125
- }));
126
- const res = await fetch(`${OLLAMA_BASE_URL}/api/chat`, {
127
- method: "POST",
128
- headers: { "Content-Type": "application/json" },
129
- body: JSON.stringify({
130
- model,
131
- messages: ollamaMessages,
132
- stream: false,
133
- options: {
134
- temperature: options?.temperature ?? 0.7,
135
- num_predict: options?.maxTokens ?? 512
136
- }
137
- }),
138
- signal: options?.signal ?? AbortSignal.timeout(3e4)
139
- });
140
- if (!res.ok) {
141
- const errorText = await res.text();
142
- throw new Error(`Ollama API error ${res.status}: ${errorText}`);
143
- }
144
- const data = await res.json();
145
- const latencyMs = Date.now() - start;
146
- return {
147
- text: data.message?.content || "(sin respuesta)",
148
- model,
149
- latencyMs,
150
- tokensUsed: data.eval_count
151
- };
152
- }
153
- };
154
-
155
- // src/ai/services/providers/GeminiProvider.ts
156
- var GEMINI_API_BASE = "https://generativelanguage.googleapis.com/v1beta";
157
- var DEFAULT_MODEL2 = "gemini-flash-lite-latest";
158
- var AVAILABLE_MODELS = [
159
- "gemini-flash-lite-latest",
160
- "gemini-flash-lite-latest-lite",
161
- "gemini-1.5-pro",
162
- "gemini-flash-latest"
163
- ];
164
- var GeminiProvider = class {
165
- id = "gemini";
166
- name = "Google Gemini";
167
- requiresApiKey = true;
168
- defaultModel = DEFAULT_MODEL2;
169
- apiKey = null;
170
- setApiKey(key) {
171
- this.apiKey = key;
172
- }
173
- async listModels() {
174
- return AVAILABLE_MODELS;
175
- }
176
- async checkStatus() {
177
- if (!this.apiKey) return "unconfigured";
178
- try {
179
- const res = await fetch(
180
- `${GEMINI_API_BASE}/models?key=${this.apiKey}`,
181
- { signal: AbortSignal.timeout(5e3) }
182
- );
183
- return res.ok ? "available" : "error";
184
- } catch {
185
- return "unavailable";
186
- }
187
- }
188
- async chat(messages, options) {
189
- if (!this.apiKey) {
190
- throw new Error("Gemini API key not configured. Set it in Settings \u2192 Providers.");
191
- }
192
- const model = options?.model || this.defaultModel;
193
- const start = Date.now();
194
- const systemMsg = messages.find((m) => m.role === "system");
195
- const conversationMsgs = messages.filter((m) => m.role !== "system");
196
- const contents = conversationMsgs.map((m) => ({
197
- role: m.role === "assistant" ? "model" : "user",
198
- parts: [{ text: m.content }]
199
- }));
200
- const body = {
201
- contents,
202
- generationConfig: {
203
- temperature: options?.temperature ?? 0.7,
204
- maxOutputTokens: options?.maxTokens ?? 2048
205
- }
206
- };
207
- if (systemMsg) {
208
- body.systemInstruction = {
209
- parts: [{ text: systemMsg.content }]
210
- };
211
- }
212
- const url = `${GEMINI_API_BASE}/models/${model}:generateContent?key=${this.apiKey}`;
213
- const res = await fetch(url, {
214
- method: "POST",
215
- headers: { "Content-Type": "application/json" },
216
- body: JSON.stringify(body),
217
- signal: options?.signal ?? AbortSignal.timeout(6e4)
218
- });
219
- if (!res.ok) {
220
- const errorBody = await res.text();
221
- throw new Error(`Gemini API error ${res.status}: ${errorBody}`);
222
- }
223
- const data = await res.json();
224
- const latencyMs = Date.now() - start;
225
- const candidate = data.candidates?.[0];
226
- const text = candidate?.content?.parts?.map((p) => p.text || "").join("") || "(sin respuesta)";
227
- const tokensUsed = (data.usageMetadata?.promptTokenCount || 0) + (data.usageMetadata?.candidatesTokenCount || 0);
228
- return {
229
- text,
230
- model,
231
- latencyMs,
232
- tokensUsed
233
- };
234
- }
235
- };
236
-
237
- // src/ai/services/providers/AnthropicProvider.ts
238
- var ANTHROPIC_API_BASE = "https://api.anthropic.com/v1";
239
- var DEFAULT_MODEL3 = "claude-sonnet-4-20250514";
240
- var API_VERSION = "2023-06-01";
241
- var AVAILABLE_MODELS2 = [
242
- "claude-sonnet-4-20250514",
243
- "claude-3-5-sonnet-20241022",
244
- "claude-3-5-haiku-20241022",
245
- "claude-3-opus-20240229"
246
- ];
247
- var AnthropicProvider = class {
248
- id = "anthropic";
249
- name = "Anthropic Claude";
250
- requiresApiKey = true;
251
- defaultModel = DEFAULT_MODEL3;
252
- apiKey = null;
253
- setApiKey(key) {
254
- this.apiKey = key;
255
- }
256
- async listModels() {
257
- return AVAILABLE_MODELS2;
258
- }
259
- async checkStatus() {
260
- if (!this.apiKey) return "unconfigured";
261
- try {
262
- const res = await fetch(`${ANTHROPIC_API_BASE}/messages`, {
263
- method: "POST",
264
- headers: {
265
- "x-api-key": this.apiKey,
266
- "anthropic-version": API_VERSION,
267
- "content-type": "application/json"
268
- },
269
- body: JSON.stringify({
270
- model: this.defaultModel,
271
- max_tokens: 1,
272
- messages: [{ role: "user", content: "ping" }]
273
- }),
274
- signal: AbortSignal.timeout(5e3)
275
- });
276
- return res.ok || res.status === 429 ? "available" : "error";
277
- } catch {
278
- return "unavailable";
279
- }
280
- }
281
- async chat(messages, options) {
282
- if (!this.apiKey) {
283
- throw new Error("Anthropic API key not configured. Set it in Settings \u2192 Providers.");
284
- }
285
- const model = options?.model || this.defaultModel;
286
- const start = Date.now();
287
- const systemMsg = messages.find((m) => m.role === "system");
288
- const conversationMsgs = messages.filter((m) => m.role !== "system").map((m) => ({
289
- role: m.role,
290
- content: m.content
291
- }));
292
- const sanitized = this._sanitizeMessages(conversationMsgs);
293
- const body = {
294
- model,
295
- max_tokens: options?.maxTokens ?? 2048,
296
- messages: sanitized
297
- };
298
- if (options?.temperature !== void 0) {
299
- body.temperature = options.temperature;
300
- }
301
- if (systemMsg) {
302
- body.system = systemMsg.content;
303
- }
304
- const res = await fetch(`${ANTHROPIC_API_BASE}/messages`, {
305
- method: "POST",
306
- headers: {
307
- "x-api-key": this.apiKey,
308
- "anthropic-version": API_VERSION,
309
- "content-type": "application/json"
310
- },
311
- body: JSON.stringify(body),
312
- signal: options?.signal ?? AbortSignal.timeout(6e4)
313
- });
314
- if (!res.ok) {
315
- const errorBody = await res.text();
316
- throw new Error(`Anthropic API error ${res.status}: ${errorBody}`);
317
- }
318
- const data = await res.json();
319
- const latencyMs = Date.now() - start;
320
- const text = (data.content || []).filter((block) => block.type === "text").map((block) => block.text).join("") || "(sin respuesta)";
321
- const tokensUsed = (data.usage?.input_tokens || 0) + (data.usage?.output_tokens || 0);
322
- return { text, model, latencyMs, tokensUsed };
323
- }
324
- /** Stream chat completion — yields text chunks */
325
- async *chatStream(messages, options) {
326
- if (!this.apiKey) {
327
- throw new Error("Anthropic API key not configured.");
328
- }
329
- const model = options?.model || this.defaultModel;
330
- const systemMsg = messages.find((m) => m.role === "system");
331
- const conversationMsgs = messages.filter((m) => m.role !== "system").map((m) => ({ role: m.role, content: m.content }));
332
- const sanitized = this._sanitizeMessages(conversationMsgs);
333
- const body = {
334
- model,
335
- max_tokens: options?.maxTokens ?? 2048,
336
- messages: sanitized,
337
- stream: true
338
- };
339
- if (options?.temperature !== void 0) body.temperature = options.temperature;
340
- if (systemMsg) body.system = systemMsg.content;
341
- const res = await fetch(`${ANTHROPIC_API_BASE}/messages`, {
342
- method: "POST",
343
- headers: {
344
- "x-api-key": this.apiKey,
345
- "anthropic-version": API_VERSION,
346
- "content-type": "application/json"
347
- },
348
- body: JSON.stringify(body),
349
- signal: options?.signal ?? AbortSignal.timeout(12e4)
350
- });
351
- if (!res.ok) {
352
- const errorBody = await res.text();
353
- throw new Error(`Anthropic stream error ${res.status}: ${errorBody}`);
354
- }
355
- const reader = res.body?.getReader();
356
- if (!reader) return;
357
- const decoder = new TextDecoder();
358
- let buffer = "";
359
- while (true) {
360
- const { done, value } = await reader.read();
361
- if (done) break;
362
- buffer += decoder.decode(value, { stream: true });
363
- const lines = buffer.split("\n");
364
- buffer = lines.pop() || "";
365
- for (const line of lines) {
366
- if (!line.startsWith("data: ")) continue;
367
- const data = line.slice(6).trim();
368
- if (data === "[DONE]") return;
369
- try {
370
- const event = JSON.parse(data);
371
- if (event.type === "content_block_delta" && event.delta?.text) {
372
- yield event.delta.text;
373
- }
374
- } catch {
375
- }
376
- }
377
- }
378
- }
379
- /** Ensure alternating user/assistant (Anthropic requires this) */
380
- _sanitizeMessages(msgs) {
381
- if (msgs.length === 0) return [{ role: "user", content: "..." }];
382
- const result = [];
383
- let lastRole = "";
384
- for (const msg of msgs) {
385
- if (msg.role === lastRole) {
386
- result[result.length - 1].content += "\n" + msg.content;
387
- } else {
388
- result.push({ ...msg });
389
- lastRole = msg.role;
390
- }
391
- }
392
- if (result[0]?.role !== "user") {
393
- result.unshift({ role: "user", content: "..." });
394
- }
395
- return result;
396
- }
397
- };
398
-
399
- // src/ai/services/providers/OpenAIProvider.ts
400
- var OPENAI_API_BASE = "https://api.openai.com/v1";
401
- var DEFAULT_MODEL4 = "gpt-4o";
402
- var AVAILABLE_MODELS3 = [
403
- "gpt-4o",
404
- "gpt-4o-mini",
405
- "gpt-4-turbo",
406
- "gpt-4",
407
- "gpt-3.5-turbo",
408
- "o3-mini"
409
- ];
410
- var OpenAIProvider = class {
411
- id = "openai";
412
- name = "OpenAI";
413
- requiresApiKey = true;
414
- defaultModel = DEFAULT_MODEL4;
415
- apiKey = null;
416
- baseUrl = OPENAI_API_BASE;
417
- setApiKey(key) {
418
- this.apiKey = key;
419
- }
420
- /** Override base URL for OpenAI-compatible endpoints */
421
- setBaseUrl(url) {
422
- this.baseUrl = url.replace(/\/$/, "");
423
- }
424
- async listModels() {
425
- if (!this.apiKey) return AVAILABLE_MODELS3;
426
- try {
427
- const res = await fetch(`${this.baseUrl}/models`, {
428
- headers: { Authorization: `Bearer ${this.apiKey}` },
429
- signal: AbortSignal.timeout(5e3)
430
- });
431
- if (!res.ok) return AVAILABLE_MODELS3;
432
- const data = await res.json();
433
- const models = (data.data || []).map((m) => m.id).filter((id) => id.startsWith("gpt-") || id.startsWith("o"));
434
- return models.length > 0 ? models : AVAILABLE_MODELS3;
435
- } catch {
436
- return AVAILABLE_MODELS3;
437
- }
438
- }
439
- async checkStatus() {
440
- if (!this.apiKey) return "unconfigured";
441
- try {
442
- const res = await fetch(`${this.baseUrl}/models`, {
443
- headers: { Authorization: `Bearer ${this.apiKey}` },
444
- signal: AbortSignal.timeout(5e3)
445
- });
446
- return res.ok ? "available" : "error";
447
- } catch {
448
- return "unavailable";
449
- }
450
- }
451
- async chat(messages, options) {
452
- if (!this.apiKey) {
453
- throw new Error("OpenAI API key not configured. Set it in Settings \u2192 Providers.");
454
- }
455
- const model = options?.model || this.defaultModel;
456
- const start = Date.now();
457
- const body = {
458
- model,
459
- messages: messages.map((m) => ({ role: m.role, content: m.content })),
460
- temperature: options?.temperature ?? 0.7,
461
- max_tokens: options?.maxTokens ?? 2048
462
- };
463
- const res = await fetch(`${this.baseUrl}/chat/completions`, {
464
- method: "POST",
465
- headers: {
466
- Authorization: `Bearer ${this.apiKey}`,
467
- "Content-Type": "application/json"
468
- },
469
- body: JSON.stringify(body),
470
- signal: options?.signal ?? AbortSignal.timeout(6e4)
471
- });
472
- if (!res.ok) {
473
- const errorBody = await res.text();
474
- throw new Error(`OpenAI API error ${res.status}: ${errorBody}`);
475
- }
476
- const data = await res.json();
477
- const latencyMs = Date.now() - start;
478
- const text = data.choices?.[0]?.message?.content || "(sin respuesta)";
479
- const tokensUsed = (data.usage?.prompt_tokens || 0) + (data.usage?.completion_tokens || 0);
480
- return { text, model, latencyMs, tokensUsed };
481
- }
482
- /** Stream chat completion — yields text chunks */
483
- async *chatStream(messages, options) {
484
- if (!this.apiKey) {
485
- throw new Error("OpenAI API key not configured.");
486
- }
487
- const model = options?.model || this.defaultModel;
488
- const body = {
489
- model,
490
- messages: messages.map((m) => ({ role: m.role, content: m.content })),
491
- temperature: options?.temperature ?? 0.7,
492
- max_tokens: options?.maxTokens ?? 2048,
493
- stream: true
494
- };
495
- const res = await fetch(`${this.baseUrl}/chat/completions`, {
496
- method: "POST",
497
- headers: {
498
- Authorization: `Bearer ${this.apiKey}`,
499
- "Content-Type": "application/json"
500
- },
501
- body: JSON.stringify(body),
502
- signal: options?.signal ?? AbortSignal.timeout(12e4)
503
- });
504
- if (!res.ok) {
505
- const errorBody = await res.text();
506
- throw new Error(`OpenAI stream error ${res.status}: ${errorBody}`);
507
- }
508
- const reader = res.body?.getReader();
509
- if (!reader) return;
510
- const decoder = new TextDecoder();
511
- let buffer = "";
512
- while (true) {
513
- const { done, value } = await reader.read();
514
- if (done) break;
515
- buffer += decoder.decode(value, { stream: true });
516
- const lines = buffer.split("\n");
517
- buffer = lines.pop() || "";
518
- for (const line of lines) {
519
- if (!line.startsWith("data: ")) continue;
520
- const data = line.slice(6).trim();
521
- if (data === "[DONE]") return;
522
- try {
523
- const chunk = JSON.parse(data);
524
- const delta = chunk.choices?.[0]?.delta?.content;
525
- if (delta) yield delta;
526
- } catch {
527
- }
528
- }
529
- }
530
- }
531
- };
532
-
533
- // src/ai/services/TokenWallet.ts
534
- var PRICING = {
535
- // Ollama / local — free
536
- "qwen2:latest": { input: 0, output: 0 },
537
- "llama3:latest": { input: 0, output: 0 },
538
- "mistral:latest": { input: 0, output: 0 },
539
- "codellama:latest": { input: 0, output: 0 },
540
- // Gemini
541
- "gemini-flash-lite-latest": { input: 0.1, output: 0.4 },
542
- "gemini-flash-lite-latest-lite": { input: 0.075, output: 0.3 },
543
- "gemini-1.5-pro": { input: 1.25, output: 5 },
544
- "gemini-flash-latest": { input: 0.075, output: 0.3 },
545
- // Anthropic
546
- "claude-sonnet-4-20250514": { input: 3, output: 15 },
547
- "claude-3-5-sonnet-20241022": { input: 3, output: 15 },
548
- "claude-3-5-haiku-20241022": { input: 0.8, output: 4 },
549
- "claude-3-opus-20240229": { input: 15, output: 75 },
550
- // OpenAI
551
- "gpt-4o": { input: 2.5, output: 10 },
552
- "gpt-4o-mini": { input: 0.15, output: 0.6 },
553
- "gpt-4-turbo": { input: 10, output: 30 },
554
- "gpt-4": { input: 30, output: 60 },
555
- "gpt-3.5-turbo": { input: 0.5, output: 1.5 },
556
- "o3-mini": { input: 1.1, output: 4.4 }
557
- };
558
- var DEFAULT_PRICING = { input: 1, output: 3 };
559
- var STORAGE_KEY = "decido-token-wallet";
560
- var MAX_HISTORY = 500;
561
- var TokenWalletImpl = class {
562
- history = [];
563
- sessionStart = Date.now();
564
- listeners = /* @__PURE__ */ new Set();
565
- constructor() {
566
- this._loadFromStorage();
567
- }
568
- // ── Record Usage ────────────────────────────────────────
569
- record(entry) {
570
- const totalTokens = entry.tokensUsed ?? (entry.inputTokens ?? 0) + (entry.outputTokens ?? 0);
571
- const inputTokens = entry.inputTokens ?? Math.round(totalTokens * 0.4);
572
- const outputTokens = entry.outputTokens ?? totalTokens - inputTokens;
573
- const pricing = this._getPricing(entry.model, entry.provider);
574
- const estimatedCostUsd = inputTokens / 1e6 * pricing.input + outputTokens / 1e6 * pricing.output;
575
- const usageEntry = {
576
- provider: entry.provider,
577
- model: entry.model,
578
- inputTokens,
579
- outputTokens,
580
- totalTokens,
581
- estimatedCostUsd,
582
- timestamp: Date.now()
583
- };
584
- this.history.push(usageEntry);
585
- if (this.history.length > MAX_HISTORY) {
586
- this.history = this.history.slice(-MAX_HISTORY);
587
- }
588
- this._saveToStorage();
589
- this._emit();
590
- console.log(
591
- `\u{1F4B0} [Wallet] ${entry.provider}/${entry.model}: ${totalTokens} tokens \u2248 $${estimatedCostUsd.toFixed(6)}`
592
- );
593
- }
594
- // ── Summary ─────────────────────────────────────────────
595
- getSummary() {
596
- const byProvider = {};
597
- let totalTokens = 0;
598
- let totalCostUsd = 0;
599
- for (const entry of this.history) {
600
- totalTokens += entry.totalTokens;
601
- totalCostUsd += entry.estimatedCostUsd;
602
- if (!byProvider[entry.provider]) {
603
- byProvider[entry.provider] = { tokens: 0, cost: 0, calls: 0 };
604
- }
605
- byProvider[entry.provider].tokens += entry.totalTokens;
606
- byProvider[entry.provider].cost += entry.estimatedCostUsd;
607
- byProvider[entry.provider].calls += 1;
608
- }
609
- return {
610
- totalTokens,
611
- totalCostUsd,
612
- totalCalls: this.history.length,
613
- byProvider,
614
- sessionStart: this.sessionStart
615
- };
616
- }
617
- getHistory() {
618
- return [...this.history];
619
- }
620
- getRecentHistory(count) {
621
- return this.history.slice(-count);
622
- }
623
- // ── Lifecycle ───────────────────────────────────────────
624
- clearHistory() {
625
- this.history = [];
626
- this.sessionStart = Date.now();
627
- this._saveToStorage();
628
- this._emit();
629
- }
630
- // ── Subscriptions ───────────────────────────────────────
631
- subscribe(listener) {
632
- this.listeners.add(listener);
633
- return () => {
634
- this.listeners.delete(listener);
635
- };
636
- }
637
- // ── Pricing Lookup ──────────────────────────────────────
638
- _getPricing(model, provider) {
639
- if (PRICING[model]) return PRICING[model];
640
- if (provider === "ollama" || provider === "mlx") {
641
- return { input: 0, output: 0 };
642
- }
643
- return DEFAULT_PRICING;
644
- }
645
- // ── Persistence ─────────────────────────────────────────
646
- _saveToStorage() {
647
- try {
648
- const data = JSON.stringify({
649
- history: this.history,
650
- sessionStart: this.sessionStart
651
- });
652
- localStorage.setItem(STORAGE_KEY, data);
653
- } catch {
654
- }
655
- }
656
- _loadFromStorage() {
657
- try {
658
- const raw = localStorage.getItem(STORAGE_KEY);
659
- if (raw) {
660
- const data = JSON.parse(raw);
661
- this.history = data.history || [];
662
- this.sessionStart = data.sessionStart || Date.now();
663
- }
664
- } catch {
665
- }
666
- }
667
- // ── Emit ────────────────────────────────────────────────
668
- _emit() {
669
- const summary = this.getSummary();
670
- for (const listener of this.listeners) {
671
- try {
672
- listener(summary);
673
- } catch {
674
- }
675
- }
676
- }
677
- };
678
- var tokenWallet = new TokenWalletImpl();
679
-
680
- // src/ai/services/InferenceRouter.ts
681
- var DEFAULT_CONFIG = {
682
- preferredBackend: "auto",
683
- maxLatencyMs: 3e4,
684
- enableFallback: true,
685
- temperature: 0.7,
686
- maxTokens: 512
687
- };
688
- var _initialized = false;
689
- function ensureProviders() {
690
- if (_initialized) return;
691
- _initialized = true;
692
- registerProvider(new OllamaProvider());
693
- registerProvider(new GeminiProvider());
694
- registerProvider(new AnthropicProvider());
695
- registerProvider(new OpenAIProvider());
696
- console.log("\u{1F50C} [InferenceRouter] 4 providers initialized (Ollama, Gemini, Anthropic, OpenAI)");
697
- }
698
- var _cachedKeys = null;
699
- async function getInvoke() {
700
- try {
701
- const tauri = await import("@tauri-apps/api/core");
702
- return tauri.invoke;
703
- } catch {
704
- return null;
705
- }
706
- }
707
- async function loadProviderKeys() {
708
- if (_cachedKeys) return _cachedKeys;
709
- try {
710
- const invoke = await getInvoke();
711
- if (invoke) {
712
- const raw = await invoke("load_provider_keys");
713
- if (raw) {
714
- _cachedKeys = JSON.parse(raw);
715
- return _cachedKeys;
716
- }
717
- }
718
- } catch (e) {
719
- console.warn("[InferenceRouter] Failed to load provider keys:", e);
720
- }
721
- try {
722
- const stored = localStorage.getItem("macia-provider-keys");
723
- if (stored) {
724
- _cachedKeys = JSON.parse(stored);
725
- return _cachedKeys;
726
- }
727
- } catch {
728
- }
729
- _cachedKeys = {};
730
- return _cachedKeys;
731
- }
732
- async function saveProviderKeys(keys) {
733
- _cachedKeys = keys;
734
- try {
735
- const invoke = await getInvoke();
736
- if (invoke) {
737
- await invoke("save_provider_keys", {
738
- json: JSON.stringify(keys, null, 2)
739
- });
740
- return;
741
- }
742
- } catch (e) {
743
- console.warn("[InferenceRouter] Failed to save provider keys via Tauri:", e);
744
- }
745
- try {
746
- localStorage.setItem("macia-provider-keys", JSON.stringify(keys));
747
- } catch {
748
- }
749
- }
750
- async function initProviders() {
751
- ensureProviders();
752
- const keys = await loadProviderKeys();
753
- const providerKeyMap = {
754
- gemini: keys.gemini,
755
- anthropic: keys.anthropic,
756
- openai: keys.openai
757
- };
758
- for (const [id, key] of Object.entries(providerKeyMap)) {
759
- if (key) {
760
- const provider = getProvider(id);
761
- provider?.setApiKey?.(key);
762
- console.log(`\u{1F511} [InferenceRouter] ${id} API key loaded`);
763
- }
764
- }
765
- }
766
- async function setProviderApiKey(providerId, apiKey) {
767
- const provider = getProvider(providerId);
768
- if (provider?.setApiKey) {
769
- provider.setApiKey(apiKey);
770
- }
771
- const keys = await loadProviderKeys();
772
- keys[providerId] = apiKey;
773
- await saveProviderKeys(keys);
774
- }
775
- async function getProviderStatuses() {
776
- ensureProviders();
777
- const statuses = {};
778
- for (const provider of getAllProviders()) {
779
- statuses[provider.id] = await provider.checkStatus();
780
- }
781
- return statuses;
782
- }
783
- function classifyTask(prompt) {
784
- const lower = prompt.toLowerCase();
785
- const toolPatterns = [
786
- "ejecuta",
787
- "revisa",
788
- "escanea",
789
- "limpia",
790
- "optimiza",
791
- "monitorea",
792
- "diagnostica",
793
- "analiza el sistema",
794
- "procesos",
795
- "disco",
796
- "memoria",
797
- "red",
798
- "seguridad",
799
- "git",
800
- "commit",
801
- "deploy",
802
- "backup"
803
- ];
804
- if (toolPatterns.some((p) => lower.includes(p))) return "tool-calling";
805
- const codePatterns = ["codigo", "funcion", "clase", "error", "debug", "refactor", "typescript", "python", "rust"];
806
- if (codePatterns.some((p) => lower.includes(p))) return "code";
807
- const analysisPatterns = ["analiza", "compara", "benchmark", "rendimiento", "metricas", "estadisticas"];
808
- if (analysisPatterns.some((p) => lower.includes(p))) return "analysis";
809
- const creativePatterns = ["genera", "crea", "escribe", "inventa", "imagina", "dise\xF1a"];
810
- if (creativePatterns.some((p) => lower.includes(p))) return "creative";
811
- return "chat";
812
- }
813
- async function routeChat(messages, config = {}) {
814
- ensureProviders();
815
- const cfg = { ...DEFAULT_CONFIG, ...config };
816
- const providerOrder = resolveProviderOrder(cfg.preferredBackend);
817
- for (const providerId of providerOrder) {
818
- const provider = getProvider(providerId);
819
- if (!provider) continue;
820
- const status = await provider.checkStatus();
821
- if (status !== "available") {
822
- if (!cfg.enableFallback) {
823
- throw new Error(`Provider ${provider.name} is ${status}`);
824
- }
825
- continue;
826
- }
827
- try {
828
- const result = await provider.chat(messages, {
829
- model: void 0,
830
- // use provider default
831
- temperature: cfg.temperature,
832
- maxTokens: cfg.maxTokens
833
- });
834
- tokenWallet.record({
835
- provider: providerId,
836
- model: result.model,
837
- tokensUsed: result.tokensUsed
838
- });
839
- return {
840
- ...result,
841
- backend: providerId
842
- };
843
- } catch (err) {
844
- console.warn(`[InferenceRouter] ${provider.name} failed:`, err);
845
- if (!cfg.enableFallback) throw err;
846
- }
847
- }
848
- throw new Error("No hay backends de IA disponibles. Inicia Ollama o configura una API key de Gemini.");
849
- }
850
- function resolveProviderOrder(preferred) {
851
- switch (preferred) {
852
- case "ollama":
853
- return ["ollama", "gemini", "anthropic", "openai"];
854
- case "gemini":
855
- return ["gemini", "ollama", "anthropic", "openai"];
856
- case "anthropic":
857
- return ["anthropic", "gemini", "ollama", "openai"];
858
- case "openai":
859
- return ["openai", "gemini", "ollama", "anthropic"];
860
- case "auto":
861
- default:
862
- return ["ollama", "gemini", "anthropic", "openai"];
863
- }
864
- }
865
- async function routeInference(prompt, config = {}) {
866
- const messages = [
867
- { role: "user", content: prompt }
868
- ];
869
- try {
870
- const result = await routeChat(messages, config);
871
- return {
872
- text: result.text,
873
- backend: result.backend,
874
- model: result.model,
875
- latencyMs: result.latencyMs,
876
- tokensPerSecond: result.tokensUsed ? result.tokensUsed / (result.latencyMs / 1e3) : 0,
877
- toolsInvoked: []
878
- };
879
- } catch {
880
- return {
881
- text: "No hay backends disponibles. Inicia Ollama o configura un modelo.",
882
- backend: "auto",
883
- model: "none",
884
- latencyMs: 0,
885
- tokensPerSecond: 0,
886
- toolsInvoked: []
887
- };
888
- }
889
- }
890
- var inferenceRouter = {
891
- route: routeInference,
892
- routeChat,
893
- classifyTask,
894
- initProviders,
895
- getProviderStatuses,
896
- setProviderApiKey,
897
- loadProviderKeys,
898
- getAllProviders,
899
- getProvider,
900
- tokenWallet
901
- };
902
-
903
- // src/kernel.ts
904
- var import_sdk = require("@decido/sdk");
905
- async function getTauriCore() {
906
- return import("@tauri-apps/api/core");
907
- }
908
- async function getTauriEvent() {
909
- return import("@tauri-apps/api/event");
910
- }
911
- var TauriTransport = class {
912
- name = "Anillo 0/1 (Tauri IPC)";
913
- isConnectedToCloud = false;
914
- unlistenFns = [];
915
- connectToSwarm(tenantId, url, token) {
916
- console.log(`[Kernel/Tauri] Conexi\xF3n nativa delegada a Core-Rust para tenant: ${tenantId}`);
917
- }
918
- async execute(tenantId, cmd, args) {
919
- const { invoke } = await getTauriCore();
920
- return invoke(cmd, { tenantId, ...args });
921
- }
922
- async notify(title, body) {
923
- try {
924
- const { sendNotification } = await import("@tauri-apps/plugin-notification");
925
- await sendNotification({ title, body });
926
- } catch (e) {
927
- console.log("[Kernel/Tauri] Notification plugin not available.");
928
- }
929
- }
930
- async vibrate(pattern) {
931
- try {
932
- const { vibrate: nativeVibrate } = await import("@tauri-apps/plugin-haptics");
933
- await nativeVibrate(pattern);
934
- } catch (e) {
935
- console.log("[Kernel/Tauri] Haptics not available on this platform.");
936
- }
937
- }
938
- async getEventHistory(tenantId, limit) {
939
- return this.execute(tenantId, "get_stream_history", { limit });
940
- }
941
- listenEvents(onEvent) {
942
- let unlistenFn = null;
943
- getTauriEvent().then(({ listen }) => {
944
- listen("orchestrator-event", (event) => {
945
- onEvent({ type: "ipc_in", channel: "orchestrator-event", payload: event.payload });
946
- }).then((fn) => {
947
- unlistenFn = fn;
948
- this.unlistenFns.push(fn);
949
- });
950
- });
951
- return () => {
952
- if (unlistenFn) {
953
- unlistenFn();
954
- this.unlistenFns = this.unlistenFns.filter((f) => f !== unlistenFn);
955
- }
956
- };
957
- }
958
- };
959
- var WebSocketTransport = class {
960
- name = "Sat\xE9lite Web (WebSocket)";
961
- isConnectedToCloud = false;
962
- sockets = {};
963
- eventListeners = /* @__PURE__ */ new Set();
964
- connectToSwarm(tenantId, url, token) {
965
- if (this.sockets[tenantId]) return;
966
- const socket = (0, import_socket.io)(url, { auth: token ? { token } : void 0 });
967
- socket.on("connect", () => {
968
- this.isConnectedToCloud = true;
969
- console.log(`\u2705 [Kernel/WS] Sat\xE9lite conectado al Enjambre del Tenant: ${tenantId}`);
970
- });
971
- socket.on("orchestrator-event", (eventPayload) => {
972
- this.eventListeners.forEach((cb) => cb({ type: "ipc_in", channel: "orchestrator-event", payload: { tenantId, ...eventPayload } }));
973
- });
974
- socket.on("rpc_broadcast", (eventPayload) => {
975
- console.log(`[Kernel] \u{1F310} Broadcast Recibido [${tenantId}]:`, eventPayload);
976
- this.eventListeners.forEach((cb) => cb({ type: "ipc_in", channel: "rpc_broadcast", payload: { tenantId, ...eventPayload } }));
977
- });
978
- this.sockets[tenantId] = socket;
979
- }
980
- async execute(tenantId, cmd, args) {
981
- const socket = this.sockets[tenantId];
982
- if (socket) {
983
- return new Promise((resolve, reject) => {
984
- socket.emit("rpc_call", { cmd, args }, (response) => {
985
- if (response.error) reject(new Error(response.error));
986
- else resolve(response.data);
987
- });
988
- });
989
- }
990
- console.warn(`[Kernel Mock] Executing ${cmd} for tenant ${tenantId}`, args);
991
- return {};
992
- }
993
- async notify(title, body) {
994
- console.log(`[Notification Mock] ${title}: ${body}`);
995
- }
996
- async vibrate(pattern) {
997
- if (typeof navigator !== "undefined" && navigator.vibrate) {
998
- navigator.vibrate(pattern === "heavy" ? 200 : 50);
999
- }
1000
- }
1001
- async getEventHistory(tenantId, limit) {
1002
- console.warn("[Kernel Mock] Cannot fetch stream history in Web Sandbox.");
1003
- return [];
1004
- }
1005
- listenEvents(onEvent) {
1006
- this.eventListeners.add(onEvent);
1007
- return () => this.eventListeners.delete(onEvent);
1008
- }
1009
- };
1010
- var DecidoKernel = class {
1011
- logs = [];
1012
- logListeners = /* @__PURE__ */ new Set();
1013
- universalListeners = /* @__PURE__ */ new Set();
1014
- transport;
1015
- rateLimits = /* @__PURE__ */ new Map();
1016
- checkRateLimit(cmd) {
1017
- const now = Date.now();
1018
- const limitWin = 1e3;
1019
- const maxCalls = 50;
1020
- let tracker = this.rateLimits.get(cmd);
1021
- if (!tracker || tracker.resetAt < now) {
1022
- tracker = { calls: 0, resetAt: now + limitWin };
1023
- this.rateLimits.set(cmd, tracker);
1024
- }
1025
- tracker.calls++;
1026
- if (tracker.calls > maxCalls) {
1027
- console.warn(`[Kernel Throttle] \u{1F6A8} Command ${cmd} blocked (Rate limit: >50 calls/sec). Possible plugin infinite loop.`);
1028
- return false;
1029
- }
1030
- return true;
1031
- }
1032
- constructor() {
1033
- const isTauri = typeof window !== "undefined" && "__TAURI_INTERNALS__" in window;
1034
- this.transport = isTauri ? new TauriTransport() : new WebSocketTransport();
1035
- console.log(`[Kernel] Entorno Detectado. Operando en Modo ${this.transport.name}.`);
1036
- this.transport.listenEvents((event) => {
1037
- if (event.type === "ipc_in") {
1038
- this.addLog({ type: "ipc_in", channel: event.channel, payload: event.payload });
1039
- this.notifyUniversalListeners(event.payload.payload || event.payload);
1040
- }
1041
- });
1042
- }
1043
- get isConnectedToCloud() {
1044
- return this.transport.isConnectedToCloud;
1045
- }
1046
- connectToSwarm(tenantId, url, token) {
1047
- this.transport.connectToSwarm(tenantId, url, token);
1048
- }
1049
- // Validador Zod y Notificador Universal
1050
- notifyUniversalListeners(rawPayload) {
1051
- const parsed = (0, import_sdk.createSafeEvent)(rawPayload);
1052
- if (!parsed.success) {
1053
- console.error("[Kernel] \u{1F6A8} Inbound Mesh Event Failed Contract Validation:", parsed.error.format());
1054
- this.universalListeners.forEach((cb) => cb({
1055
- type: "system_alert",
1056
- source: "kernel-bridge",
1057
- payload: {
1058
- level: "error",
1059
- title: "Contract Violation",
1060
- message: "A received event did not match the strict schema. Ignored."
1061
- }
1062
- }));
1063
- return;
1064
- }
1065
- this.universalListeners.forEach((cb) => cb(parsed.data));
1066
- }
1067
- injectEvent(payload) {
1068
- this.notifyUniversalListeners(payload);
1069
- }
1070
- addLog(entry) {
1071
- const log = {
1072
- id: typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(2, 9),
1073
- timestamp: Date.now(),
1074
- ...entry
1075
- };
1076
- this.logs = [log, ...this.logs].slice(0, 500);
1077
- this.logListeners.forEach((listener) => listener(this.logs));
1078
- }
1079
- onLogsChange(callback) {
1080
- this.logListeners.add(callback);
1081
- callback(this.logs);
1082
- return () => this.logListeners.delete(callback);
1083
- }
1084
- async execute(tenantIdOrCmd, cmdOrArgs, argsOpts) {
1085
- let tenantId = "system";
1086
- let cmd = tenantIdOrCmd;
1087
- let args = cmdOrArgs;
1088
- if (typeof cmdOrArgs === "string") {
1089
- tenantId = tenantIdOrCmd;
1090
- cmd = cmdOrArgs;
1091
- args = argsOpts;
1092
- }
1093
- if (!this.checkRateLimit(cmd)) {
1094
- throw new Error(`[Kernel Throttle] Request dropped for ${cmd} (Rate limit exceeded)`);
1095
- }
1096
- const start = performance.now();
1097
- try {
1098
- const result = await this.transport.execute(tenantId, cmd, args);
1099
- this.addLog({ type: "ipc_out", channel: cmd, payload: { tenantId, ...args }, duration: performance.now() - start });
1100
- return result;
1101
- } catch (error) {
1102
- this.addLog({ type: "error", channel: cmd, payload: { tenantId, args, error }, duration: performance.now() - start });
1103
- throw error;
1104
- }
1105
- }
1106
- async notify(title, body) {
1107
- return this.transport.notify(title, body);
1108
- }
1109
- async vibrate(pattern = "medium") {
1110
- return this.transport.vibrate(pattern);
1111
- }
1112
- async captureScreen(tenantId = "system") {
1113
- return this.execute(tenantId, "capture_screen");
1114
- }
1115
- async getMachineId(tenantId = "system") {
1116
- return this.execute(tenantId, "get_machine_id");
1117
- }
1118
- async initiateSingularity(tenantId = "system") {
1119
- return this.execute(tenantId, "initiate_singularity");
1120
- }
1121
- async broadcastMessage(tenantId, message) {
1122
- return this.execute(tenantId, "broadcast_message", { message });
1123
- }
1124
- async fetchMemories(tenantId = "system", query, limit = 20) {
1125
- return this.execute(tenantId, "fetch_memories", { query, limit });
1126
- }
1127
- async storeMemory(tenantId = "system", text, metadata = {}) {
1128
- return this.execute(tenantId, "store_memory", { text, metadata });
1129
- }
1130
- async deleteMemory(tenantId = "system", id) {
1131
- return this.execute(tenantId, "delete_memory", { id });
1132
- }
1133
- async reasonWithMLX(base64Image, prompt, previousError) {
1134
- let finalPrompt = prompt;
1135
- if (previousError) {
1136
- finalPrompt += `
1137
-
1138
- WARNING: Your previous response failed to parse as JSON with the following error: ${previousError}. Fix the JSON syntax and return ONLY a valid JSON object.`;
1139
- }
1140
- try {
1141
- const response = await fetch("http://127.0.0.1:8080/v1/chat/completions", {
1142
- method: "POST",
1143
- headers: { "Content-Type": "application/json" },
1144
- body: JSON.stringify({
1145
- model: "llava",
1146
- messages: [{
1147
- role: "user",
1148
- content: [
1149
- { type: "text", text: finalPrompt },
1150
- { type: "image_url", image_url: { url: base64Image } }
1151
- ]
1152
- }],
1153
- max_tokens: 150,
1154
- temperature: 0.1
1155
- })
1156
- });
1157
- const data = await response.json();
1158
- let content = data.choices[0].message.content.trim();
1159
- if (content.startsWith("```json")) content = content.replace(/```json\n?/, "").replace(/```$/, "").trim();
1160
- else if (content.startsWith("```")) content = content.replace(/```\n?/, "").replace(/```$/, "").trim();
1161
- return JSON.parse(content);
1162
- } catch (e) {
1163
- console.error("[MLX Bridge] Reasoning failed:", e);
1164
- throw e;
1165
- }
1166
- }
1167
- async executeAction(tenantId = "system", action) {
1168
- return this.execute(tenantId, "execute_action", { action });
1169
- }
1170
- async runAutonomousStep(tenantId = "system", objective) {
1171
- const image = await this.captureScreen(tenantId);
1172
- let decision = null, retries = 0, lastError = null;
1173
- const instruction = `You are an AI OS agent. Based on the screen image, what action should be taken to achieve: "${objective}"? Return ONLY a strict JSON object of the action, no markdown. E.g. {"type": "mouseClick", "x": 100, "y": 200, "button": "left", "count": 1}`;
1174
- while (retries <= 2 && !decision) {
1175
- try {
1176
- decision = await this.reasonWithMLX(image, instruction, lastError || void 0);
1177
- } catch (error) {
1178
- lastError = error.message || String(error);
1179
- retries++;
1180
- }
1181
- }
1182
- if (!decision) return;
1183
- await this.executeAction(tenantId, decision);
1184
- }
1185
- async askAI(prompt, taskType = "reasoning", forceProvider) {
1186
- const result = await inferenceRouter.route(prompt, { preferredBackend: forceProvider || "auto" });
1187
- return result.text;
1188
- }
1189
- onEvent(callback) {
1190
- this.universalListeners.add(callback);
1191
- return () => this.universalListeners.delete(callback);
1192
- }
1193
- async getEventHistory(tenantId, limit = 50) {
1194
- return this.transport.getEventHistory(tenantId, limit);
1195
- }
1196
- };
1197
- var kernel = new DecidoKernel();
1198
-
1199
- // src/ai/services/OllamaService.ts
1200
- var OLLAMA_BASE_URL2 = "http://localhost:11434";
1201
- var DEFAULT_MODEL5 = "qwen2:latest";
1202
- function buildSystemPrompt() {
1203
- const liveContext = buildLiveContext();
1204
- const toolSchemas = "";
1205
- return `- Est\xE1s integrado en DecidoOS, una plataforma empresarial de escritorio
6
+ WARNING: Your previous response failed to parse as JSON with the following error: ${s}. Fix the JSON syntax and return ONLY a valid JSON object.`);try{let i=(await(await fetch("http://127.0.0.1:8080/v1/chat/completions",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:"llava",messages:[{role:"user",content:[{type:"text",text:r},{type:"image_url",image_url:{url:e}}]}],max_tokens:150,temperature:.1})})).json()).choices[0].message.content.trim();return i.startsWith("```json")?i=i.replace(/```json\n?/,"").replace(/```$/,"").trim():i.startsWith("```")&&(i=i.replace(/```\n?/,"").replace(/```$/,"").trim()),JSON.parse(i)}catch(o){throw console.error("[MLX Bridge] Reasoning failed:",o),o}}async executeAction(e="system",t){return this.execute(e,"execute_action",{action:t})}async runAutonomousStep(e="system",t){let s=await this.captureScreen(e),r=null,o=0,a=null,i=`You are an AI OS agent. Based on the screen image, what action should be taken to achieve: "${t}"? Return ONLY a strict JSON object of the action, no markdown. E.g. {"type": "mouseClick", "x": 100, "y": 200, "button": "left", "count": 1}`;for(;o<=2&&!r;)try{r=await this.reasonWithMLX(s,i,a||void 0)}catch(d){a=d.message||String(d),o++}r&&await this.executeAction(e,r)}async askAI(e,t="reasoning",s){return(await K.route(e,{preferredBackend:s||"auto"})).text}onEvent(e){return this.universalListeners.add(e),()=>this.universalListeners.delete(e)}async getEventHistory(e,t=50){return this.transport.getEventHistory(e,t)}},F=new oe;var ae="http://localhost:11434",yt="qwen2:latest";function Se(){return`- Est\xE1s integrado en DecidoOS, una plataforma empresarial de escritorio
1206
7
  - Corres localmente en la m\xE1quina del usuario \u2014 todo es privado
1207
8
  - Puedes ejecutar herramientas del sistema para ayudar al usuario
1208
9
 
1209
- ${liveContext}
10
+ ${vt()}
1210
11
 
1211
12
  ## Herramientas disponibles
1212
13
 
1213
- ${toolSchemas}
14
+
1214
15
 
1215
16
  ## Reglas de respuesta
1216
17
  1. Responde siempre en espa\xF1ol a menos que el usuario hable en otro idioma
@@ -1219,471 +20,9 @@ ${toolSchemas}
1219
20
  4. Mant\xE9n respuestas bajo 300 palabras
1220
21
  5. Si no sabes algo, dilo honestamente
1221
22
  6. NO inventes resultados de herramientas \u2014 espera el resultado real
1222
- 7. Cuando el usuario pregunte sobre el estado del sistema, USA LOS DATOS del "Estado actual del sistema" que tienes arriba \u2014 esos son datos reales y recientes`;
1223
- }
1224
- function buildLiveContext() {
1225
- const parts = ["## Estado actual del sistema"];
1226
- const now = (/* @__PURE__ */ new Date()).toLocaleString("es-CO", { hour12: false });
1227
- parts.push(`Hora actual: ${now}`);
1228
- try {
1229
- const watchdogModule = globalThis.__systemWatchdog;
1230
- if (watchdogModule) {
1231
- const snapshot = watchdogModule.getLastSnapshot?.();
1232
- if (snapshot) {
1233
- const metrics = [];
1234
- if (snapshot.cpuPercent !== null) metrics.push(`CPU: ${snapshot.cpuPercent.toFixed(1)}%`);
1235
- if (snapshot.memoryPercent !== null) metrics.push(`Memoria: ${snapshot.memoryPercent.toFixed(1)}%`);
1236
- if (snapshot.diskFreeGB !== null) metrics.push(`Disco libre: ${snapshot.diskFreeGB.toFixed(1)} GB`);
1237
- if (snapshot.connectionCount !== null) metrics.push(`Conexiones de red: ${snapshot.connectionCount}`);
1238
- if (metrics.length > 0) {
1239
- parts.push(`M\xE9tricas del sistema: ${metrics.join(" | ")}`);
1240
- }
1241
- }
1242
- const alerts = watchdogModule.getAlerts?.()?.filter((a) => !a.dismissed).slice(-5) ?? [];
1243
- if (alerts.length > 0) {
1244
- parts.push("Alertas recientes:");
1245
- for (const alert of alerts) {
1246
- const emoji = alert.severity === "critical" ? "\u{1F6A8}" : "\u26A0\uFE0F";
1247
- parts.push(` ${emoji} ${alert.title}`);
1248
- }
1249
- }
1250
- }
1251
- } catch {
1252
- }
1253
- try {
1254
- const appStore = globalThis.__appStore;
1255
- const ctx = appStore?.getState?.()?.contextSnapshot;
1256
- if (ctx) {
1257
- if (ctx.canvasNodeCount > 0) parts.push(`Canvas: ${ctx.canvasNodeCount} nodos`);
1258
- if (ctx.gitBranch) parts.push(`Git: rama ${ctx.gitBranch}${ctx.gitModifiedFiles ? ` (${ctx.gitModifiedFiles} archivos modificados)` : ""}`);
1259
- if (ctx.activeInsights > 0) parts.push(`Insights activos: ${ctx.activeInsights}`);
1260
- if (ctx.criticalInsightsSummary?.length > 0) {
1261
- parts.push("Insights cr\xEDticos: " + ctx.criticalInsightsSummary.slice(0, 3).join("; "));
1262
- }
1263
- }
1264
- } catch {
1265
- }
1266
- try {
1267
- const memoryModule = globalThis.__agentMemory;
1268
- if (memoryModule) {
1269
- const memContext = memoryModule.buildMemoryContext?.();
1270
- if (memContext) {
1271
- parts.push("");
1272
- parts.push(memContext);
1273
- }
1274
- }
1275
- } catch {
1276
- }
1277
- return parts.join("\n");
1278
- }
1279
- function parseToolCalls(message) {
1280
- const calls = [];
1281
- if (message?.tool_calls && Array.isArray(message.tool_calls)) {
1282
- for (const tc of message.tool_calls) {
1283
- if (tc.function?.name) {
1284
- calls.push({
1285
- name: tc.function.name,
1286
- args: tc.function.arguments || {}
1287
- });
1288
- }
1289
- }
1290
- }
1291
- return calls;
1292
- }
1293
- function stripToolCalls(text) {
1294
- if (!text) return "";
1295
- return text.replace(/<tool_call>[\s\S]*?<\/tool_call>/g, "").trim();
1296
- }
1297
- var conversationHistory = [];
1298
- var MAX_HISTORY2 = 20;
1299
- function addToHistory(msg) {
1300
- conversationHistory.push(msg);
1301
- if (conversationHistory.length > MAX_HISTORY2) {
1302
- conversationHistory = conversationHistory.slice(-MAX_HISTORY2);
1303
- }
1304
- }
1305
- function clearConversationHistory() {
1306
- conversationHistory = [];
1307
- }
1308
- async function isOllamaAvailable() {
1309
- try {
1310
- const res = await fetch(`${OLLAMA_BASE_URL2}/api/tags`, {
1311
- signal: AbortSignal.timeout(2e3)
1312
- });
1313
- return res.ok;
1314
- } catch {
1315
- return false;
1316
- }
1317
- }
1318
- async function listModels() {
1319
- try {
1320
- const res = await fetch(`${OLLAMA_BASE_URL2}/api/tags`);
1321
- if (!res.ok) return [];
1322
- const data = await res.json();
1323
- return (data.models || []).map((m) => m.name);
1324
- } catch {
1325
- return [];
1326
- }
1327
- }
1328
- async function chat(userMessage, options) {
1329
- const userMsg = { role: "user", content: userMessage };
1330
- addToHistory(userMsg);
1331
- const messages = [
1332
- { role: "system", content: buildSystemPrompt() },
1333
- ...conversationHistory
1334
- ];
1335
- try {
1336
- const result = await routeChat(messages, {
1337
- temperature: options?.temperature ?? 0.7,
1338
- maxTokens: 512
1339
- });
1340
- const assistantContent = result?.text || "";
1341
- addToHistory({ role: "assistant", content: assistantContent });
1342
- if (result) {
1343
- console.log(`\u{1F9E0}[Chat] Response via ${result.backend} (${result.model}) in ${result.latencyMs} ms`);
1344
- }
1345
- return assistantContent;
1346
- } catch (error) {
1347
- if (error instanceof DOMException && error.name === "TimeoutError") {
1348
- return "\u23F3 La respuesta del modelo tard\xF3 demasiado. Intenta con una pregunta m\xE1s corta.";
1349
- }
1350
- throw error;
1351
- }
1352
- }
1353
- async function* chatStream(userMessage, options) {
1354
- const model = options?.model || DEFAULT_MODEL5;
1355
- const userMsg = { role: "user", content: userMessage };
1356
- addToHistory(userMsg);
1357
- const messages = [
1358
- { role: "system", content: buildSystemPrompt() },
1359
- ...conversationHistory
1360
- ];
1361
- const res = await fetch(`${OLLAMA_BASE_URL2}/api/chat`, {
1362
- method: "POST",
1363
- headers: { "Content-Type": "application/json" },
1364
- body: JSON.stringify({
1365
- model,
1366
- messages,
1367
- stream: true,
1368
- options: {
1369
- temperature: options?.temperature ?? 0.7,
1370
- num_predict: 512
1371
- }
1372
- })
1373
- });
1374
- if (!res.ok || !res.body) {
1375
- throw new Error(`Ollama stream error: ${res.status}`);
1376
- }
1377
- const reader = res.body.getReader();
1378
- const decoder = new TextDecoder();
1379
- let fullContent = "";
1380
- try {
1381
- while (true) {
1382
- const { done, value } = await reader.read();
1383
- if (done) break;
1384
- const chunk = decoder.decode(value, { stream: true });
1385
- const lines = chunk.split("\n").filter(Boolean);
1386
- for (const line of lines) {
1387
- try {
1388
- const data = JSON.parse(line);
1389
- if (data.message?.content) {
1390
- fullContent += data.message.content;
1391
- yield data.message.content;
1392
- }
1393
- } catch {
1394
- }
1395
- }
1396
- }
1397
- } finally {
1398
- reader.releaseLock();
1399
- }
1400
- addToHistory({ role: "assistant", content: fullContent });
1401
- }
1402
-
1403
- // src/ai/services/MLXBridge.ts
1404
- async function runShellCommand(cmd, args) {
1405
- try {
1406
- const { invoke } = await import("@tauri-apps/api/core");
1407
- const result = await invoke("run_shell_command", {
1408
- command: cmd,
1409
- args
1410
- });
1411
- return result;
1412
- } catch (err) {
1413
- console.error("[MLXBridge] Shell command failed:", err);
1414
- throw err;
1415
- }
1416
- }
1417
- var OLLAMA_URL = "http://localhost:11434";
1418
- async function ollamaChat(model, prompt, temperature = 0.7) {
1419
- const start = Date.now();
1420
- try {
1421
- const res = await fetch(`${OLLAMA_URL}/api/chat`, {
1422
- method: "POST",
1423
- headers: { "Content-Type": "application/json" },
1424
- body: JSON.stringify({
1425
- model,
1426
- messages: [{ role: "user", content: prompt }],
1427
- stream: false,
1428
- options: { temperature }
1429
- })
1430
- });
1431
- const data = await res.json();
1432
- const latencyMs = Date.now() - start;
1433
- return {
1434
- text: data.message?.content ?? "",
1435
- tokensPerSecond: data.eval_count ? data.eval_count / (latencyMs / 1e3) : 0,
1436
- totalTokens: data.eval_count ?? 0,
1437
- latencyMs,
1438
- model
1439
- };
1440
- } catch {
1441
- return { text: "Error: Ollama no disponible", tokensPerSecond: 0, totalTokens: 0, latencyMs: Date.now() - start, model };
1442
- }
1443
- }
1444
- async function ollamaListModels() {
1445
- try {
1446
- const res = await fetch(`${OLLAMA_URL}/api/tags`);
1447
- if (!res.ok) return [];
1448
- const data = await res.json();
1449
- return (data.models || []).map((m) => m.name);
1450
- } catch {
1451
- return [];
1452
- }
1453
- }
1454
- async function ollamaPull(model) {
1455
- await fetch(`${OLLAMA_URL}/api/pull`, {
1456
- method: "POST",
1457
- headers: { "Content-Type": "application/json" },
1458
- body: JSON.stringify({ name: model })
1459
- });
1460
- }
1461
- async function mlxGenerate(model, prompt) {
1462
- const start = Date.now();
1463
- try {
1464
- const output = await runShellCommand("python3", [
1465
- "-m",
1466
- "mlx_lm.generate",
1467
- "--model",
1468
- model,
1469
- "--prompt",
1470
- prompt,
1471
- "--max-tokens",
1472
- "512"
1473
- ]);
1474
- return {
1475
- text: output,
1476
- tokensPerSecond: 0,
1477
- // parsed from output in production
1478
- totalTokens: output.split(" ").length,
1479
- latencyMs: Date.now() - start,
1480
- model
1481
- };
1482
- } catch (err) {
1483
- return { text: `MLX Error: ${err}`, tokensPerSecond: 0, totalTokens: 0, latencyMs: Date.now() - start, model };
1484
- }
1485
- }
1486
- async function listAvailableModels() {
1487
- const models = [];
1488
- const ollamaModels = await ollamaListModels();
1489
- for (const name of ollamaModels) {
1490
- models.push({
1491
- name,
1492
- family: "llm",
1493
- path: `ollama:${name}`,
1494
- loaded: true
1495
- });
1496
- }
1497
- const mlxCatalog = [
1498
- { name: "Qwen2.5-7B-Instruct-4bit", family: "llm", paramCount: "7B", quantization: "4bit", path: "mlx-community/Qwen2.5-7B-Instruct-4bit", loaded: false },
1499
- { name: "Mistral-7B-Instruct-v0.3-4bit", family: "llm", paramCount: "7B", quantization: "4bit", path: "mlx-community/Mistral-7B-Instruct-v0.3-4bit", loaded: false },
1500
- { name: "Llama-3.2-3B-Instruct-4bit", family: "llm", paramCount: "3B", quantization: "4bit", path: "mlx-community/Llama-3.2-3B-Instruct-4bit", loaded: false },
1501
- { name: "Mixtral-8x7B-Instruct-v0.1-4bit", family: "llm", paramCount: "46.7B", quantization: "4bit", path: "mlx-community/Mixtral-8x7B-Instruct-v0.1-4bit", loaded: false },
1502
- { name: "Whisper-large-v3", family: "audio", path: "mlx-community/whisper-large-v3", loaded: false },
1503
- { name: "CLIP-ViT-B-32", family: "vision", path: "openai/clip-vit-base-patch32", loaded: false },
1504
- { name: "FLUX.1-schnell-4bit", family: "image-gen", path: "mlx-community/FLUX.1-schnell-4bit-quantized", loaded: false },
1505
- { name: "Stable-Diffusion-XL", family: "image-gen", path: "mlx-community/sdxl-turbo", loaded: false }
1506
- ];
1507
- models.push(...mlxCatalog);
1508
- return models;
1509
- }
1510
- async function runInference(modelPath, prompt, options) {
1511
- if (modelPath.startsWith("ollama:")) {
1512
- const model = modelPath.replace("ollama:", "");
1513
- return ollamaChat(model, prompt, options?.temperature);
1514
- }
1515
- return mlxGenerate(modelPath, prompt);
1516
- }
1517
- async function compareModels(modelA, modelB, prompt) {
1518
- const [a, b] = await Promise.all([
1519
- runInference(modelA, prompt),
1520
- runInference(modelB, prompt)
1521
- ]);
1522
- return { a, b };
1523
- }
1524
- async function pullModel(model) {
1525
- if (model.startsWith("ollama:")) {
1526
- await ollamaPull(model.replace("ollama:", ""));
1527
- } else {
1528
- await runShellCommand("huggingface-cli", ["download", model]);
1529
- }
1530
- }
1531
- async function benchmarkModel(modelPath) {
1532
- const testPrompts = [
1533
- "Explain quantum computing in one paragraph.",
1534
- "Write a Python function to sort a list.",
1535
- "What is the capital of Colombia?"
1536
- ];
1537
- let totalTokens = 0;
1538
- let totalMs = 0;
1539
- for (const prompt of testPrompts) {
1540
- const result = await runInference(modelPath, prompt);
1541
- totalTokens += result.totalTokens;
1542
- totalMs += result.latencyMs;
1543
- }
1544
- return {
1545
- model: modelPath,
1546
- promptTokens: testPrompts.join(" ").split(" ").length,
1547
- generatedTokens: totalTokens,
1548
- tokensPerSecond: totalTokens / (totalMs / 1e3),
1549
- latencyMs: totalMs / testPrompts.length,
1550
- memoryMb: 0
1551
- // would need system metrics
1552
- };
1553
- }
1554
- function startLoRATraining(config) {
1555
- let stopped = false;
1556
- let progressCallback = null;
1557
- const totalSteps = (config.epochs ?? 10) * 100;
1558
- let step = 0;
1559
- const start = Date.now();
1560
- const interval = setInterval(() => {
1561
- if (stopped || step >= totalSteps) {
1562
- clearInterval(interval);
1563
- return;
1564
- }
1565
- step++;
1566
- const progress = {
1567
- epoch: Math.floor(step / 100) + 1,
1568
- totalEpochs: config.epochs ?? 10,
1569
- step,
1570
- totalSteps,
1571
- loss: 2.5 * Math.exp(-step / 200) + 0.3 + Math.random() * 0.1,
1572
- learningRate: config.learningRate ?? 1e-5,
1573
- tokensPerSecond: 150 + Math.random() * 50,
1574
- elapsedMs: Date.now() - start
1575
- };
1576
- progressCallback?.(progress);
1577
- }, 500);
1578
- return {
1579
- stop: () => {
1580
- stopped = true;
1581
- clearInterval(interval);
1582
- },
1583
- onProgress: (cb) => {
1584
- progressCallback = cb;
1585
- }
1586
- };
1587
- }
1588
-
1589
- // src/ai/services/EmbeddingService.ts
1590
- var OLLAMA_URL2 = "http://localhost:11434";
1591
- var EMBED_MODEL = "nomic-embed-text";
1592
- var TFIDF_DIMS = 256;
1593
- async function ollamaEmbed(text) {
1594
- const start = Date.now();
1595
- try {
1596
- const res = await fetch(`${OLLAMA_URL2}/api/embed`, {
1597
- method: "POST",
1598
- headers: { "Content-Type": "application/json" },
1599
- body: JSON.stringify({ model: EMBED_MODEL, input: text })
1600
- });
1601
- if (!res.ok) throw new Error(`Ollama embed failed: ${res.status}`);
1602
- const data = await res.json();
1603
- const vector = data.embeddings?.[0] || data.embedding || [];
1604
- return {
1605
- vector,
1606
- dimensions: vector.length,
1607
- model: EMBED_MODEL,
1608
- latencyMs: Date.now() - start
1609
- };
1610
- } catch (err) {
1611
- console.warn("[EmbeddingService] Ollama not available, falling back to TF-IDF", err);
1612
- return tfidfEmbed(text);
1613
- }
1614
- }
1615
- function tfidfEmbed(text) {
1616
- const start = Date.now();
1617
- const tokens = text.toLowerCase().replace(/[^\w\s]/g, "").split(/\s+/).filter((t) => t.length > 2);
1618
- const vector = new Array(TFIDF_DIMS).fill(0);
1619
- for (const token of tokens) {
1620
- let hash = 0;
1621
- for (let i = 0; i < token.length; i++) {
1622
- hash = (hash << 5) - hash + token.charCodeAt(i) | 0;
1623
- }
1624
- const idx = Math.abs(hash) % TFIDF_DIMS;
1625
- vector[idx] += 1 / tokens.length;
1626
- }
1627
- const norm = Math.sqrt(vector.reduce((s, v) => s + v * v, 0));
1628
- if (norm > 0) {
1629
- for (let i = 0; i < vector.length; i++) vector[i] /= norm;
1630
- }
1631
- return {
1632
- vector,
1633
- dimensions: TFIDF_DIMS,
1634
- model: "tfidf-fallback",
1635
- latencyMs: Date.now() - start
1636
- };
1637
- }
1638
- var preferredBackend = "ollama";
1639
- async function embed(text) {
1640
- if (preferredBackend === "ollama") {
1641
- return ollamaEmbed(text);
1642
- }
1643
- return tfidfEmbed(text);
1644
- }
1645
- async function embedBatch(texts) {
1646
- return Promise.all(texts.map((t) => embed(t)));
1647
- }
1648
- function setBackend(backend) {
1649
- preferredBackend = backend;
1650
- }
1651
- function cosineSimilarity(a, b) {
1652
- if (a.length !== b.length) return 0;
1653
- let dot = 0, normA = 0, normB = 0;
1654
- for (let i = 0; i < a.length; i++) {
1655
- dot += a[i] * b[i];
1656
- normA += a[i] * a[i];
1657
- normB += b[i] * b[i];
1658
- }
1659
- const denom = Math.sqrt(normA) * Math.sqrt(normB);
1660
- return denom === 0 ? 0 : dot / denom;
1661
- }
1662
- var embeddingService = { embed, embedBatch, setBackend, cosineSimilarity };
1663
-
1664
- // src/ai/services/LocalAgentResponder.ts
1665
- var GREETING_PATTERNS = [
1666
- /^(hola|hey|hi|hello|buenas|qué tal|que tal|saludos)\s*(decido|decidoos|agente|asistente)?/i,
1667
- /^(buenos?\s*(días|tardes|noches))/i,
1668
- /^(órale|oye|ey)\s*(decido)?/i
1669
- ];
1670
- var HELP_PATTERNS = [
1671
- /^(ayuda|help|qué puedes hacer|que puedes hacer|comandos|tools|herramientas)/i,
1672
- /^(qué|que)\s*(sabes|haces|puedes)/i
1673
- ];
1674
- var SYSTEM_PATTERNS = [
1675
- /^(estado|status|sistema|system)\s*(del\s*sistema)?/i,
1676
- /^(cómo|como)\s*(estás|estas|está|va)/i
1677
- ];
1678
- function getTimeGreeting() {
1679
- const hour = (/* @__PURE__ */ new Date()).getHours();
1680
- if (hour < 12) return "Buenos d\xEDas";
1681
- if (hour < 18) return "Buenas tardes";
1682
- return "Buenas noches";
1683
- }
1684
- function getWelcomeMessage() {
1685
- const greeting = getTimeGreeting();
1686
- return `${greeting}, operador. Soy **DecidoOS Agent** \u{1F9E0}
23
+ 7. Cuando el usuario pregunte sobre el estado del sistema, USA LOS DATOS del "Estado actual del sistema" que tienes arriba \u2014 esos son datos reales y recientes`}function vt(){let n=["## Estado actual del sistema"],e=new Date().toLocaleString("es-CO",{hour12:!1});n.push(`Hora actual: ${e}`);try{let t=globalThis.__systemWatchdog;if(t){let s=t.getLastSnapshot?.();if(s){let o=[];s.cpuPercent!==null&&o.push(`CPU: ${s.cpuPercent.toFixed(1)}%`),s.memoryPercent!==null&&o.push(`Memoria: ${s.memoryPercent.toFixed(1)}%`),s.diskFreeGB!==null&&o.push(`Disco libre: ${s.diskFreeGB.toFixed(1)} GB`),s.connectionCount!==null&&o.push(`Conexiones de red: ${s.connectionCount}`),o.length>0&&n.push(`M\xE9tricas del sistema: ${o.join(" | ")}`)}let r=t.getAlerts?.()?.filter(o=>!o.dismissed).slice(-5)??[];if(r.length>0){n.push("Alertas recientes:");for(let o of r){let a=o.severity==="critical"?"\u{1F6A8}":"\u26A0\uFE0F";n.push(` ${a} ${o.title}`)}}}}catch{}try{let s=globalThis.__appStore?.getState?.()?.contextSnapshot;s&&(s.canvasNodeCount>0&&n.push(`Canvas: ${s.canvasNodeCount} nodos`),s.gitBranch&&n.push(`Git: rama ${s.gitBranch}${s.gitModifiedFiles?` (${s.gitModifiedFiles} archivos modificados)`:""}`),s.activeInsights>0&&n.push(`Insights activos: ${s.activeInsights}`),s.criticalInsightsSummary?.length>0&&n.push("Insights cr\xEDticos: "+s.criticalInsightsSummary.slice(0,3).join("; ")))}catch{}try{let t=globalThis.__agentMemory;if(t){let s=t.buildMemoryContext?.();s&&(n.push(""),n.push(s))}}catch{}return n.join(`
24
+ `)}function Ce(n){let e=[];if(n?.tool_calls&&Array.isArray(n.tool_calls))for(let t of n.tool_calls)t.function?.name&&e.push({name:t.function.name,args:t.function.arguments||{}});return e}function Te(n){return n?n.replace(/<tool_call>[\s\S]*?<\/tool_call>/g,"").trim():""}var S=[],Me=20;function j(n){S.push(n),S.length>Me&&(S=S.slice(-Me))}function _e(){S=[]}async function Le(){try{return(await fetch(`${ae}/api/tags`,{signal:AbortSignal.timeout(2e3)})).ok}catch{return!1}}async function Re(){try{let n=await fetch(`${ae}/api/tags`);return n.ok?((await n.json()).models||[]).map(t=>t.name):[]}catch{return[]}}async function ie(n,e){j({role:"user",content:n});let s=[{role:"system",content:Se()},...S];try{let r=await A(s,{temperature:e?.temperature??.7,maxTokens:512}),o=r?.text||"";return j({role:"assistant",content:o}),r&&console.log(`\u{1F9E0}[Chat] Response via ${r.backend} (${r.model}) in ${r.latencyMs} ms`),o}catch(r){if(r instanceof DOMException&&r.name==="TimeoutError")return"\u23F3 La respuesta del modelo tard\xF3 demasiado. Intenta con una pregunta m\xE1s corta.";throw r}}async function*ce(n,e){let t=e?.model||yt;j({role:"user",content:n});let r=[{role:"system",content:Se()},...S],o=await fetch(`${ae}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:t,messages:r,stream:!0,options:{temperature:e?.temperature??.7,num_predict:512}})});if(!o.ok||!o.body)throw new Error(`Ollama stream error: ${o.status}`);let a=o.body.getReader(),i=new TextDecoder,d="";try{for(;;){let{done:l,value:h}=await a.read();if(l)break;let g=i.decode(h,{stream:!0}).split(`
25
+ `).filter(Boolean);for(let v of g)try{let p=JSON.parse(v);p.message?.content&&(d+=p.message.content,yield p.message.content)}catch{}}}finally{a.releaseLock()}j({role:"assistant",content:d})}async function Ae(n,e){try{let{invoke:t}=await import("@tauri-apps/api/core");return await t("run_shell_command",{command:n,args:e})}catch(t){throw console.error("[MLXBridge] Shell command failed:",t),t}}var le="http://localhost:11434";async function xt(n,e,t=.7){let s=Date.now();try{let o=await(await fetch(`${le}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:n,messages:[{role:"user",content:e}],stream:!1,options:{temperature:t}})})).json(),a=Date.now()-s;return{text:o.message?.content??"",tokensPerSecond:o.eval_count?o.eval_count/(a/1e3):0,totalTokens:o.eval_count??0,latencyMs:a,model:n}}catch{return{text:"Error: Ollama no disponible",tokensPerSecond:0,totalTokens:0,latencyMs:Date.now()-s,model:n}}}async function bt(){try{let n=await fetch(`${le}/api/tags`);return n.ok?((await n.json()).models||[]).map(t=>t.name):[]}catch{return[]}}async function Pt(n){await fetch(`${le}/api/pull`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:n})})}async function kt(n,e){let t=Date.now();try{let s=await Ae("python3",["-m","mlx_lm.generate","--model",n,"--prompt",e,"--max-tokens","512"]);return{text:s,tokensPerSecond:0,totalTokens:s.split(" ").length,latencyMs:Date.now()-t,model:n}}catch(s){return{text:`MLX Error: ${s}`,tokensPerSecond:0,totalTokens:0,latencyMs:Date.now()-t,model:n}}}async function Ie(){let n=[],e=await bt();for(let s of e)n.push({name:s,family:"llm",path:`ollama:${s}`,loaded:!0});let t=[{name:"Qwen2.5-7B-Instruct-4bit",family:"llm",paramCount:"7B",quantization:"4bit",path:"mlx-community/Qwen2.5-7B-Instruct-4bit",loaded:!1},{name:"Mistral-7B-Instruct-v0.3-4bit",family:"llm",paramCount:"7B",quantization:"4bit",path:"mlx-community/Mistral-7B-Instruct-v0.3-4bit",loaded:!1},{name:"Llama-3.2-3B-Instruct-4bit",family:"llm",paramCount:"3B",quantization:"4bit",path:"mlx-community/Llama-3.2-3B-Instruct-4bit",loaded:!1},{name:"Mixtral-8x7B-Instruct-v0.1-4bit",family:"llm",paramCount:"46.7B",quantization:"4bit",path:"mlx-community/Mixtral-8x7B-Instruct-v0.1-4bit",loaded:!1},{name:"Whisper-large-v3",family:"audio",path:"mlx-community/whisper-large-v3",loaded:!1},{name:"CLIP-ViT-B-32",family:"vision",path:"openai/clip-vit-base-patch32",loaded:!1},{name:"FLUX.1-schnell-4bit",family:"image-gen",path:"mlx-community/FLUX.1-schnell-4bit-quantized",loaded:!1},{name:"Stable-Diffusion-XL",family:"image-gen",path:"mlx-community/sdxl-turbo",loaded:!1}];return n.push(...t),n}async function B(n,e,t){if(n.startsWith("ollama:")){let s=n.replace("ollama:","");return xt(s,e,t?.temperature)}return kt(n,e)}async function Ne(n,e,t){let[s,r]=await Promise.all([B(n,t),B(e,t)]);return{a:s,b:r}}async function Ee(n){n.startsWith("ollama:")?await Pt(n.replace("ollama:","")):await Ae("huggingface-cli",["download",n])}async function $e(n){let e=["Explain quantum computing in one paragraph.","Write a Python function to sort a list.","What is the capital of Colombia?"],t=0,s=0;for(let r of e){let o=await B(n,r);t+=o.totalTokens,s+=o.latencyMs}return{model:n,promptTokens:e.join(" ").split(" ").length,generatedTokens:t,tokensPerSecond:t/(s/1e3),latencyMs:s/e.length,memoryMb:0}}function Oe(n){let e=!1,t=null,s=(n.epochs??10)*100,r=0,o=Date.now(),a=setInterval(()=>{if(e||r>=s){clearInterval(a);return}r++;let i={epoch:Math.floor(r/100)+1,totalEpochs:n.epochs??10,step:r,totalSteps:s,loss:2.5*Math.exp(-r/200)+.3+Math.random()*.1,learningRate:n.learningRate??1e-5,tokensPerSecond:150+Math.random()*50,elapsedMs:Date.now()-o};t?.(i)},500);return{stop:()=>{e=!0,clearInterval(a)},onProgress:i=>{t=i}}}var wt="http://localhost:11434",De="nomic-embed-text";async function Mt(n){let e=Date.now();try{let t=await fetch(`${wt}/api/embed`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:De,input:n})});if(!t.ok)throw new Error(`Ollama embed failed: ${t.status}`);let s=await t.json(),r=s.embeddings?.[0]||s.embedding||[];return{vector:r,dimensions:r.length,model:De,latencyMs:Date.now()-e}}catch(t){return console.warn("[EmbeddingService] Ollama not available, falling back to TF-IDF",t),Be(n)}}function Be(n){let e=Date.now(),t=n.toLowerCase().replace(/[^\w\s]/g,"").split(/\s+/).filter(o=>o.length>2),s=new Array(256).fill(0);for(let o of t){let a=0;for(let d=0;d<o.length;d++)a=(a<<5)-a+o.charCodeAt(d)|0;let i=Math.abs(a)%256;s[i]+=1/t.length}let r=Math.sqrt(s.reduce((o,a)=>o+a*a,0));if(r>0)for(let o=0;o<s.length;o++)s[o]/=r;return{vector:s,dimensions:256,model:"tfidf-fallback",latencyMs:Date.now()-e}}var He="ollama";async function Ue(n){return He==="ollama"?Mt(n):Be(n)}async function St(n){return Promise.all(n.map(e=>Ue(e)))}function Ct(n){He=n}function Tt(n,e){if(n.length!==e.length)return 0;let t=0,s=0,r=0;for(let a=0;a<n.length;a++)t+=n[a]*e[a],s+=n[a]*n[a],r+=e[a]*e[a];let o=Math.sqrt(s)*Math.sqrt(r);return o===0?0:t/o}var Ke={embed:Ue,embedBatch:St,setBackend:Ct,cosineSimilarity:Tt};var _t=[/^(hola|hey|hi|hello|buenas|qué tal|que tal|saludos)\s*(decido|decidoos|agente|asistente)?/i,/^(buenos?\s*(días|tardes|noches))/i,/^(órale|oye|ey)\s*(decido)?/i],Lt=[/^(ayuda|help|qué puedes hacer|que puedes hacer|comandos|tools|herramientas)/i,/^(qué|que)\s*(sabes|haces|puedes)/i],Rt=[/^(estado|status|sistema|system)\s*(del\s*sistema)?/i,/^(cómo|como)\s*(estás|estas|está|va)/i];function At(){let n=new Date().getHours();return n<12?"Buenos d\xEDas":n<18?"Buenas tardes":"Buenas noches"}function It(){return`${At()}, operador. Soy **DecidoOS Agent** \u{1F9E0}
1687
26
 
1688
27
  Estoy en l\xEDnea y operativo. Aqu\xED est\xE1 mi reporte de estado:
1689
28
 
@@ -1692,10 +31,7 @@ Estoy en l\xEDnea y operativo. Aqu\xED est\xE1 mi reporte de estado:
1692
31
 
1693
32
  Puedo auditar tu seguridad, escanear tu flota de procesos y m\xE1s.
1694
33
 
1695
- Escribe **"ayuda"** para ver mis comandos principales.`;
1696
- }
1697
- function getHelpMessage() {
1698
- return `\u{1F6E0}\uFE0F **Capacidades de DecidoOS Agent**
34
+ Escribe **"ayuda"** para ver mis comandos principales.`}function Nt(){return`\u{1F6E0}\uFE0F **Capacidades de DecidoOS Agent**
1699
35
 
1700
36
  \u2022 "escanea puertos" \u2014 Auditar puertos abiertos
1701
37
  \u2022 "escanea red" \u2014 Monitorear conexiones de red
@@ -1704,102 +40,14 @@ function getHelpMessage() {
1704
40
  \u2022 "estado" \u2014 Estado del sistema
1705
41
 
1706
42
  **Voice Mode:**
1707
- Presiona el bot\xF3n verde \u{1F4DE} para activar conversaci\xF3n por voz con Cortex.`;
1708
- }
1709
- async function getSystemStatus() {
1710
- let cpuInfo = "N/A";
1711
- let memInfo = "N/A";
1712
- try {
1713
- const { invoke } = await import("@tauri-apps/api/core");
1714
- const cpu = await invoke("get_cpu_usage");
1715
- const mem = await invoke("get_memory_usage");
1716
- cpuInfo = `${cpu.usage?.toFixed(1)}%`;
1717
- memInfo = `${(mem.used / 1024 / 1024 / 1024).toFixed(1)} GB / ${(mem.total / 1024 / 1024 / 1024).toFixed(1)} GB`;
1718
- } catch {
1719
- cpuInfo = "(requiere Tauri)";
1720
- memInfo = "(requiere Tauri)";
1721
- }
1722
- return `\u{1F4CA} **Estado del Sistema DecidoOS**
43
+ Presiona el bot\xF3n verde \u{1F4DE} para activar conversaci\xF3n por voz con Cortex.`}async function Et(){let n="N/A",e="N/A";try{let{invoke:t}=await import("@tauri-apps/api/core"),s=await t("get_cpu_usage"),r=await t("get_memory_usage");n=`${s.usage?.toFixed(1)}%`,e=`${(r.used/1024/1024/1024).toFixed(1)} GB / ${(r.total/1024/1024/1024).toFixed(1)} GB`}catch{n="(requiere Tauri)",e="(requiere Tauri)"}return`\u{1F4CA} **Estado del Sistema DecidoOS**
1723
44
 
1724
- \u25B8 **CPU**: ${cpuInfo}
1725
- \u25B8 **RAM**: ${memInfo}
45
+ \u25B8 **CPU**: ${n}
46
+ \u25B8 **RAM**: ${e}
1726
47
  \u25B8 **Cortex**: Local mode
1727
- \u25B8 **Uptime Session**: ${getSessionUptime()}
48
+ \u25B8 **Uptime Session**: ${$t()}
1728
49
 
1729
- Todo operativo. \xBFNecesitas algo m\xE1s?`;
1730
- }
1731
- function getSessionUptime() {
1732
- const uptime = performance.now();
1733
- const minutes = Math.floor(uptime / 6e4);
1734
- const hours = Math.floor(minutes / 60);
1735
- if (hours > 0) return `${hours}h ${minutes % 60}m`;
1736
- return `${minutes}m`;
1737
- }
1738
- async function processLocalMessage(input) {
1739
- const trimmed = input.trim();
1740
- if (GREETING_PATTERNS.some((p) => p.test(trimmed))) {
1741
- return { text: getWelcomeMessage() };
1742
- }
1743
- if (HELP_PATTERNS.some((p) => p.test(trimmed))) {
1744
- return { text: getHelpMessage() };
1745
- }
1746
- if (SYSTEM_PATTERNS.some((p) => p.test(trimmed))) {
1747
- const status = await getSystemStatus();
1748
- return { text: status };
1749
- }
1750
- const execMatch = trimmed.match(/^(ejecuta|run|exec|corre)\s+(.+)/i);
1751
- if (execMatch) {
1752
- const command = execMatch[2];
1753
- return {
1754
- text: `\u26A1 Ejecutando: \`${command}\`...`,
1755
- toolCalls: [{ name: "tactical.execute_command", args: { command } }]
1756
- };
1757
- }
1758
- const scanMatch = trimmed.match(/^(escanea|scan|audita|audit)\s*(puertos|ports)/i);
1759
- if (scanMatch) {
1760
- return {
1761
- text: "\u{1F50D} Escaneando puertos del sistema...",
1762
- toolCalls: [{ name: "security.audit_ports", args: { includeLoopback: false } }]
1763
- };
1764
- }
1765
- const networkMatch = trimmed.match(/^(escanea|scan|monitorea|monitor)\s*(red|network|conexiones|connections)/i);
1766
- if (networkMatch) {
1767
- return {
1768
- text: "\u{1F310} Analizando conexiones de red...",
1769
- toolCalls: [{ name: "security.network_monitor", args: {} }]
1770
- };
1771
- }
1772
- const fleetMatch = trimmed.match(/^(escanea|scan)\s*(flota|fleet|procesos|processes)/i);
1773
- if (fleetMatch) {
1774
- return {
1775
- text: "\u{1F6F8} Escaneando flotilla de agentes...",
1776
- toolCalls: [{ name: "tactical.scan_fleet", args: {} }]
1777
- };
1778
- }
1779
- const listMatch = trimmed.match(/^(lista|list|muestra|show)\s*(tareas|tasks|playbooks)/i);
1780
- if (listMatch) {
1781
- return {
1782
- text: "\u{1F4CB} Listando tareas...",
1783
- toolCalls: [{ name: "tactical.list_tasks", args: { status: "all" } }]
1784
- };
1785
- }
1786
- const forensicMatch = trimmed.match(/^(analiza|analyze|forense|forensic)\s*(proceso|process)?\s*(\d+)?/i);
1787
- if (forensicMatch) {
1788
- const pid = forensicMatch[3] ? parseInt(forensicMatch[3]) : void 0;
1789
- return {
1790
- text: pid ? `\u{1F52C} Analizando proceso PID ${pid}...` : "\u{1F52C} Escaneando procesos sospechosos...",
1791
- toolCalls: [{ name: "security.forensic_scan", args: { pid, deep: true } }]
1792
- };
1793
- }
1794
- const vulnMatch = trimmed.match(/^(vulnerabilidades|vulnerabilities|vuln|audit\s*npm)/i);
1795
- if (vulnMatch) {
1796
- return {
1797
- text: "\u{1F6E1}\uFE0F Escaneando vulnerabilidades en dependencias...",
1798
- toolCalls: [{ name: "security.scan_vulnerabilities", args: {} }]
1799
- };
1800
- }
1801
- return {
1802
- text: `\u{1F916} Entendido: "${trimmed.slice(0, 100)}"
50
+ Todo operativo. \xBFNecesitas algo m\xE1s?`}function $t(){let n=performance.now(),e=Math.floor(n/6e4),t=Math.floor(e/60);return t>0?`${t}h ${e%60}m`:`${e}m`}async function Fe(n){let e=n.trim();if(_t.some(l=>l.test(e)))return{text:It()};if(Lt.some(l=>l.test(e)))return{text:Nt()};if(Rt.some(l=>l.test(e)))return{text:await Et()};let t=e.match(/^(ejecuta|run|exec|corre)\s+(.+)/i);if(t){let l=t[2];return{text:`\u26A1 Ejecutando: \`${l}\`...`,toolCalls:[{name:"tactical.execute_command",args:{command:l}}]}}if(e.match(/^(escanea|scan|audita|audit)\s*(puertos|ports)/i))return{text:"\u{1F50D} Escaneando puertos del sistema...",toolCalls:[{name:"security.audit_ports",args:{includeLoopback:!1}}]};if(e.match(/^(escanea|scan|monitorea|monitor)\s*(red|network|conexiones|connections)/i))return{text:"\u{1F310} Analizando conexiones de red...",toolCalls:[{name:"security.network_monitor",args:{}}]};if(e.match(/^(escanea|scan)\s*(flota|fleet|procesos|processes)/i))return{text:"\u{1F6F8} Escaneando flotilla de agentes...",toolCalls:[{name:"tactical.scan_fleet",args:{}}]};if(e.match(/^(lista|list|muestra|show)\s*(tareas|tasks|playbooks)/i))return{text:"\u{1F4CB} Listando tareas...",toolCalls:[{name:"tactical.list_tasks",args:{status:"all"}}]};let i=e.match(/^(analiza|analyze|forense|forensic)\s*(proceso|process)?\s*(\d+)?/i);if(i){let l=i[3]?parseInt(i[3]):void 0;return{text:l?`\u{1F52C} Analizando proceso PID ${l}...`:"\u{1F52C} Escaneando procesos sospechosos...",toolCalls:[{name:"security.forensic_scan",args:{pid:l,deep:!0}}]}}return e.match(/^(vulnerabilidades|vulnerabilities|vuln|audit\s*npm)/i)?{text:"\u{1F6E1}\uFE0F Escaneando vulnerabilidades en dependencias...",toolCalls:[{name:"security.scan_vulnerabilities",args:{}}]}:{text:`\u{1F916} Entendido: "${e.slice(0,100)}"
1803
51
 
1804
52
  No tengo una respuesta local para esto. Para respuestas inteligentes con IA, conecta a **Cortex** (mindframe-cortex).
1805
53
 
@@ -1807,787 +55,12 @@ Mientras tanto, prueba:
1807
55
  \u2022 "ayuda" \u2014 ver mis capacidades
1808
56
  \u2022 "ejecuta ls" \u2014 ejecutar un comando
1809
57
  \u2022 "escanea puertos" \u2014 auditar seguridad
1810
- \u2022 "estado" \u2014 ver estado del sistema`
1811
- };
1812
- }
1813
-
1814
- // src/ai/services/PeerMesh.ts
1815
- var import_uuid = require("uuid");
1816
- var DEFAULT_PORT = 9876;
1817
- var HEARTBEAT_INTERVAL = 1e4;
1818
- var PEER_TIMEOUT = 3e4;
1819
- var MAX_PEERS = 8;
1820
- var PeerMeshImpl = class {
1821
- myId = (0, import_uuid.v4)();
1822
- myName = this._getHostname();
1823
- peers = /* @__PURE__ */ new Map();
1824
- connections = /* @__PURE__ */ new Map();
1825
- isHostingFlag = false;
1826
- isConnectedFlag = false;
1827
- heartbeatTimer = null;
1828
- cleanupTimer = null;
1829
- // Event listeners
1830
- peersListeners = /* @__PURE__ */ new Set();
1831
- hostingListeners = /* @__PURE__ */ new Set();
1832
- connectionListeners = /* @__PURE__ */ new Set();
1833
- resultListeners = /* @__PURE__ */ new Set();
1834
- // ── Host Mode ─────────────────────────────────────────
1835
- /**
1836
- * Start hosting a peer group.
1837
- * Note: In browser WebSocket server is not available. This method
1838
- * is a placeholder for Tauri (Rust-side server). In browser mode,
1839
- * we simulate by acting as a "relay" through a shared connection.
1840
- */
1841
- startHost(port = DEFAULT_PORT) {
1842
- if (this.isHostingFlag) return;
1843
- this.isHostingFlag = true;
1844
- this._emitHosting(true);
1845
- this._startHeartbeat();
1846
- console.log(`\u{1F310} [PeerMesh] Hosting on port ${port} (peer: ${this.myId.slice(0, 8)})`);
1847
- console.log(`\u{1F310} [PeerMesh] \u26A0\uFE0F Browser-mode: WebSocket server requires Tauri backend.`);
1848
- console.log(`\u{1F310} [PeerMesh] Use connectToPeer() from another instance to connect.`);
1849
- }
1850
- stopHost() {
1851
- if (!this.isHostingFlag) return;
1852
- this.isHostingFlag = false;
1853
- this._stopHeartbeat();
1854
- this._broadcast({ type: "leave", peerId: this.myId, peerName: this.myName });
1855
- this._disconnectAll();
1856
- this._emitHosting(false);
1857
- console.log("\u{1F310} [PeerMesh] Stopped hosting");
1858
- }
1859
- // ── Client Mode ───────────────────────────────────────
1860
- connectToPeer(address) {
1861
- if (this.connections.size >= MAX_PEERS) {
1862
- console.warn(`\u{1F310} [PeerMesh] Max peers (${MAX_PEERS}) reached`);
1863
- return;
1864
- }
1865
- const wsUrl = address.startsWith("ws") ? address : `ws://${address}`;
1866
- console.log(`\u{1F310} [PeerMesh] Connecting to ${wsUrl}...`);
1867
- try {
1868
- const ws = new WebSocket(wsUrl);
1869
- ws.onopen = () => {
1870
- console.log(`\u{1F310} [PeerMesh] \u2705 Connected to ${address}`);
1871
- this.connections.set(address, ws);
1872
- this.isConnectedFlag = true;
1873
- this._emitConnection(true);
1874
- this._send(ws, {
1875
- type: "join",
1876
- peerId: this.myId,
1877
- peerName: this.myName
1878
- });
1879
- this._startHeartbeat();
1880
- };
1881
- ws.onmessage = (event) => {
1882
- try {
1883
- const msg = JSON.parse(event.data);
1884
- this._handleMessage(msg, address);
1885
- } catch {
1886
- }
1887
- };
1888
- ws.onclose = () => {
1889
- console.log(`\u{1F310} [PeerMesh] Disconnected from ${address}`);
1890
- this.connections.delete(address);
1891
- for (const [id, peer] of this.peers) {
1892
- if (peer.address === address) {
1893
- this.peers.delete(id);
1894
- }
1895
- }
1896
- this._emitPeers();
1897
- if (this.connections.size === 0) {
1898
- this.isConnectedFlag = false;
1899
- this._emitConnection(false);
1900
- this._stopHeartbeat();
1901
- }
1902
- };
1903
- ws.onerror = (err) => {
1904
- console.error(`\u{1F310} [PeerMesh] Connection error to ${address}:`, err);
1905
- };
1906
- } catch (err) {
1907
- console.error(`\u{1F310} [PeerMesh] Failed to connect:`, err);
1908
- }
1909
- }
1910
- disconnect() {
1911
- this._broadcast({ type: "leave", peerId: this.myId, peerName: this.myName });
1912
- this._disconnectAll();
1913
- }
1914
- // ── Share Results ─────────────────────────────────────
1915
- shareResult(result) {
1916
- const fullResult = {
1917
- ...result,
1918
- peerId: this.myId,
1919
- timestamp: Date.now()
1920
- };
1921
- this._broadcast({
1922
- type: "share-result",
1923
- peerId: this.myId,
1924
- peerName: this.myName,
1925
- payload: fullResult
1926
- });
1927
- console.log(`\u{1F310} [PeerMesh] Shared result to ${this.connections.size} peers`);
1928
- }
1929
- // ── Event Subscriptions ───────────────────────────────
1930
- onPeersChanged(cb) {
1931
- this.peersListeners.add(cb);
1932
- return () => {
1933
- this.peersListeners.delete(cb);
1934
- };
1935
- }
1936
- onHostingChanged(cb) {
1937
- this.hostingListeners.add(cb);
1938
- return () => {
1939
- this.hostingListeners.delete(cb);
1940
- };
1941
- }
1942
- onConnectionChanged(cb) {
1943
- this.connectionListeners.add(cb);
1944
- return () => {
1945
- this.connectionListeners.delete(cb);
1946
- };
1947
- }
1948
- onSharedResult(cb) {
1949
- this.resultListeners.add(cb);
1950
- return () => {
1951
- this.resultListeners.delete(cb);
1952
- };
1953
- }
1954
- // ── Getters ───────────────────────────────────────────
1955
- getPeers() {
1956
- return Array.from(this.peers.values());
1957
- }
1958
- getMyId() {
1959
- return this.myId;
1960
- }
1961
- getMyName() {
1962
- return this.myName;
1963
- }
1964
- // ── Message Handling ──────────────────────────────────
1965
- _handleMessage(msg, fromAddress) {
1966
- switch (msg.type) {
1967
- case "join":
1968
- this.peers.set(msg.peerId, {
1969
- id: msg.peerId,
1970
- name: msg.peerName,
1971
- address: fromAddress,
1972
- joinedAt: Date.now(),
1973
- lastSeen: Date.now()
1974
- });
1975
- this._emitPeers();
1976
- console.log(`\u{1F310} [PeerMesh] Peer joined: ${msg.peerName} (${msg.peerId.slice(0, 8)})`);
1977
- break;
1978
- case "leave":
1979
- this.peers.delete(msg.peerId);
1980
- this._emitPeers();
1981
- console.log(`\u{1F310} [PeerMesh] Peer left: ${msg.peerName}`);
1982
- break;
1983
- case "heartbeat":
1984
- if (this.peers.has(msg.peerId)) {
1985
- this.peers.get(msg.peerId).lastSeen = Date.now();
1986
- }
1987
- break;
1988
- case "share-result":
1989
- if (msg.payload) {
1990
- const result = msg.payload;
1991
- for (const cb of this.resultListeners) {
1992
- try {
1993
- cb(result);
1994
- } catch {
1995
- }
1996
- }
1997
- }
1998
- break;
1999
- case "peers-update":
2000
- if (Array.isArray(msg.payload)) {
2001
- for (const p of msg.payload) {
2002
- if (p.id !== this.myId && !this.peers.has(p.id)) {
2003
- this.peers.set(p.id, p);
2004
- }
2005
- }
2006
- this._emitPeers();
2007
- }
2008
- break;
2009
- }
2010
- }
2011
- // ── Internal Helpers ──────────────────────────────────
2012
- _send(ws, msg) {
2013
- if (ws.readyState === WebSocket.OPEN) {
2014
- ws.send(JSON.stringify(msg));
2015
- }
2016
- }
2017
- _broadcast(msg) {
2018
- for (const ws of this.connections.values()) {
2019
- this._send(ws, msg);
2020
- }
2021
- }
2022
- _disconnectAll() {
2023
- for (const ws of this.connections.values()) {
2024
- try {
2025
- ws.close();
2026
- } catch {
2027
- }
2028
- }
2029
- this.connections.clear();
2030
- this.peers.clear();
2031
- this.isConnectedFlag = false;
2032
- this._stopHeartbeat();
2033
- this._emitPeers();
2034
- this._emitConnection(false);
2035
- }
2036
- _startHeartbeat() {
2037
- if (this.heartbeatTimer) return;
2038
- this.heartbeatTimer = setInterval(() => {
2039
- this._broadcast({
2040
- type: "heartbeat",
2041
- peerId: this.myId,
2042
- peerName: this.myName
2043
- });
2044
- }, HEARTBEAT_INTERVAL);
2045
- this.cleanupTimer = setInterval(() => {
2046
- const now = Date.now();
2047
- let changed = false;
2048
- for (const [id, peer] of this.peers) {
2049
- if (now - peer.lastSeen > PEER_TIMEOUT) {
2050
- this.peers.delete(id);
2051
- changed = true;
2052
- console.log(`\u{1F310} [PeerMesh] Peer timed out: ${peer.name}`);
2053
- }
2054
- }
2055
- if (changed) this._emitPeers();
2056
- }, PEER_TIMEOUT);
2057
- }
2058
- _stopHeartbeat() {
2059
- if (this.heartbeatTimer) {
2060
- clearInterval(this.heartbeatTimer);
2061
- this.heartbeatTimer = null;
2062
- }
2063
- if (this.cleanupTimer) {
2064
- clearInterval(this.cleanupTimer);
2065
- this.cleanupTimer = null;
2066
- }
2067
- }
2068
- // ── Event Emitters ────────────────────────────────────
2069
- _emitPeers() {
2070
- const list = this.getPeers();
2071
- for (const cb of this.peersListeners) {
2072
- try {
2073
- cb(list);
2074
- } catch {
2075
- }
2076
- }
2077
- }
2078
- _emitHosting(hosting) {
2079
- for (const cb of this.hostingListeners) {
2080
- try {
2081
- cb(hosting);
2082
- } catch {
2083
- }
2084
- }
2085
- }
2086
- _emitConnection(connected) {
2087
- for (const cb of this.connectionListeners) {
2088
- try {
2089
- cb(connected);
2090
- } catch {
2091
- }
2092
- }
2093
- }
2094
- // ── Hostname ──────────────────────────────────────────
2095
- _getHostname() {
2096
- try {
2097
- return `user-${Math.random().toString(36).slice(2, 6)}`;
2098
- } catch {
2099
- return "anonymous";
2100
- }
2101
- }
2102
- };
2103
- var peerMesh = new PeerMeshImpl();
2104
-
2105
- // src/ai/hooks/useTokenWallet.ts
2106
- var import_react = require("react");
2107
- function useTokenWallet() {
2108
- const [summary, setSummary] = (0, import_react.useState)(tokenWallet.getSummary());
2109
- (0, import_react.useEffect)(() => {
2110
- const unsubscribe = tokenWallet.subscribe(setSummary);
2111
- return unsubscribe;
2112
- }, []);
2113
- const clearHistory = (0, import_react.useCallback)(() => {
2114
- tokenWallet.clearHistory();
2115
- }, []);
2116
- const getRecentHistory = (0, import_react.useCallback)((count = 20) => {
2117
- return tokenWallet.getRecentHistory(count);
2118
- }, []);
2119
- return {
2120
- summary,
2121
- clearHistory,
2122
- getRecentHistory,
2123
- totalTokens: summary.totalTokens,
2124
- totalCost: summary.totalCostUsd,
2125
- totalCalls: summary.totalCalls,
2126
- byProvider: summary.byProvider
2127
- };
2128
- }
2129
-
2130
- // src/ai/hooks/usePeerMesh.ts
2131
- var import_react2 = require("react");
2132
- function usePeerMesh() {
2133
- const [peers, setPeers] = (0, import_react2.useState)([]);
2134
- const [isHosting, setIsHosting] = (0, import_react2.useState)(false);
2135
- const [isConnected, setIsConnected] = (0, import_react2.useState)(false);
2136
- const [sharedResults, setSharedResults] = (0, import_react2.useState)([]);
2137
- const resultsRef = (0, import_react2.useRef)(sharedResults);
2138
- resultsRef.current = sharedResults;
2139
- (0, import_react2.useEffect)(() => {
2140
- const unsubPeers = peerMesh.onPeersChanged((newPeers) => {
2141
- setPeers([...newPeers]);
2142
- });
2143
- const unsubHosting = peerMesh.onHostingChanged((hosting) => {
2144
- setIsHosting(hosting);
2145
- });
2146
- const unsubConnected = peerMesh.onConnectionChanged((connected) => {
2147
- setIsConnected(connected);
2148
- });
2149
- const unsubResult = peerMesh.onSharedResult((result) => {
2150
- setSharedResults((prev) => [...prev.slice(-49), result]);
2151
- });
2152
- return () => {
2153
- unsubPeers();
2154
- unsubHosting();
2155
- unsubConnected();
2156
- unsubResult();
2157
- };
2158
- }, []);
2159
- const startHost = (0, import_react2.useCallback)((port) => {
2160
- peerMesh.startHost(port);
2161
- }, []);
2162
- const stopHost = (0, import_react2.useCallback)(() => {
2163
- peerMesh.stopHost();
2164
- }, []);
2165
- const connectToPeer = (0, import_react2.useCallback)((address) => {
2166
- peerMesh.connectToPeer(address);
2167
- }, []);
2168
- const disconnect = (0, import_react2.useCallback)(() => {
2169
- peerMesh.disconnect();
2170
- }, []);
2171
- const shareResult = (0, import_react2.useCallback)((result) => {
2172
- peerMesh.shareResult(result);
2173
- }, []);
2174
- return {
2175
- peers,
2176
- isHosting,
2177
- isConnected,
2178
- sharedResults,
2179
- peerCount: peers.length,
2180
- startHost,
2181
- stopHost,
2182
- connectToPeer,
2183
- disconnect,
2184
- shareResult
2185
- };
2186
- }
2187
-
2188
- // src/ai/components/TokenWalletPanel.tsx
2189
- var import_react3 = require("react");
2190
- var import_jsx_runtime = require("react/jsx-runtime");
2191
- var PROVIDER_COLORS = {
2192
- ollama: "text-accent-green",
2193
- gemini: "text-accent-blue",
2194
- anthropic: "text-accent-purple",
2195
- openai: "text-accent-cyan",
2196
- mlx: "text-accent-amber"
2197
- };
2198
- var PROVIDER_BG = {
2199
- ollama: "bg-emerald-500/10",
2200
- gemini: "bg-blue-500/10",
2201
- anthropic: "bg-purple-500/10",
2202
- openai: "bg-cyan-500/10",
2203
- mlx: "bg-amber-500/10"
2204
- };
2205
- var PROVIDER_ICONS = {
2206
- ollama: "\u{1F999}",
2207
- gemini: "\u2728",
2208
- anthropic: "\u{1F9E0}",
2209
- openai: "\u26A1",
2210
- mlx: "\u{1F34E}"
2211
- };
2212
- function formatCost(usd) {
2213
- if (usd === 0) return "FREE";
2214
- if (usd < 0.01) return `$${usd.toFixed(6)}`;
2215
- if (usd < 1) return `$${usd.toFixed(4)}`;
2216
- return `$${usd.toFixed(2)}`;
2217
- }
2218
- function formatTokens(n) {
2219
- if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
2220
- if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
2221
- return String(n);
2222
- }
2223
- function timeAgo(ts) {
2224
- const diff = Date.now() - ts;
2225
- if (diff < 6e4) return "just now";
2226
- if (diff < 36e5) return `${Math.floor(diff / 6e4)}m ago`;
2227
- if (diff < 864e5) return `${Math.floor(diff / 36e5)}h ago`;
2228
- return `${Math.floor(diff / 864e5)}d ago`;
2229
- }
2230
- function TokenWalletPanel() {
2231
- const { summary, totalTokens, totalCost, totalCalls, byProvider, clearHistory, getRecentHistory } = useTokenWallet();
2232
- const [showHistory, setShowHistory] = (0, import_react3.useState)(false);
2233
- const providers2 = Object.entries(byProvider).sort((a, b) => b[1].cost - a[1].cost);
2234
- const history = showHistory ? getRecentHistory(15) : [];
2235
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-3 p-4 bg-surface-secondary rounded-2xl border border-border-subtle", children: [
2236
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between", children: [
2237
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
2238
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-lg", children: "\u{1F4B0}" }),
2239
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "text-sm font-semibold text-text-primary", children: "Token Wallet" })
2240
- ] }),
2241
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2242
- "button",
2243
- {
2244
- onClick: clearHistory,
2245
- className: "text-[10px] px-2 py-0.5 rounded-md bg-surface-glass text-text-muted\n hover:bg-surface-tertiary hover:text-text-secondary transition-colors",
2246
- children: "Clear"
2247
- }
2248
- )
2249
- ] }),
2250
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "grid grid-cols-3 gap-2", children: [
2251
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatCard, { label: "Tokens", value: formatTokens(totalTokens), accent: "text-accent-cyan" }),
2252
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatCard, { label: "Cost", value: formatCost(totalCost), accent: totalCost === 0 ? "text-accent-green" : "text-accent-amber" }),
2253
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatCard, { label: "Calls", value: String(totalCalls), accent: "text-accent-blue" })
2254
- ] }),
2255
- providers2.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-1.5 mt-1", children: [
2256
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[10px] font-medium text-text-muted uppercase tracking-wider", children: "By Provider" }),
2257
- providers2.map(([id, data]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2258
- "div",
2259
- {
2260
- className: `flex items-center justify-between px-3 py-2 rounded-xl ${PROVIDER_BG[id] || "bg-surface-glass"} transition-colors`,
2261
- children: [
2262
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
2263
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-sm", children: PROVIDER_ICONS[id] || "\u{1F916}" }),
2264
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `text-xs font-medium ${PROVIDER_COLORS[id] || "text-text-primary"}`, children: id.charAt(0).toUpperCase() + id.slice(1) })
2265
- ] }),
2266
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-3 text-[11px]", children: [
2267
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "text-text-muted", children: [
2268
- data.calls,
2269
- " calls"
2270
- ] }),
2271
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-text-secondary font-mono", children: formatTokens(data.tokens) }),
2272
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-semibold text-text-primary font-mono", children: formatCost(data.cost) })
2273
- ] })
2274
- ]
2275
- },
2276
- id
2277
- ))
2278
- ] }),
2279
- providers2.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col items-center justify-center py-6 text-text-muted gap-1", children: [
2280
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-2xl opacity-50", children: "\u{1F4CA}" }),
2281
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-xs", children: "No AI calls recorded yet" })
2282
- ] }),
2283
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2284
- "button",
2285
- {
2286
- onClick: () => setShowHistory(!showHistory),
2287
- className: "text-[10px] text-text-muted hover:text-text-secondary transition-colors self-center",
2288
- children: showHistory ? "\u25B2 Hide recent" : "\u25BC Show recent calls"
2289
- }
2290
- ),
2291
- showHistory && history.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-col gap-1 max-h-48 overflow-y-auto", children: history.reverse().map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HistoryRow, { entry }, `${entry.timestamp}-${i}`)) })
2292
- ] });
2293
- }
2294
- function StatCard({ label, value, accent }) {
2295
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col items-center p-2 rounded-xl bg-surface-tertiary/50", children: [
2296
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[9px] text-text-muted uppercase tracking-wider", children: label }),
2297
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `text-base font-bold font-mono ${accent}`, children: value })
2298
- ] });
2299
- }
2300
- function HistoryRow({ entry }) {
2301
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between px-2 py-1.5 rounded-lg bg-surface-glass text-[10px]", children: [
2302
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-1.5", children: [
2303
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: PROVIDER_ICONS[entry.provider] || "\u{1F916}" }),
2304
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-text-secondary font-mono", children: entry.model.split("/").pop()?.slice(0, 18) })
2305
- ] }),
2306
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
2307
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-text-muted", children: formatTokens(entry.totalTokens) }),
2308
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-text-primary font-mono", children: formatCost(entry.estimatedCostUsd) }),
2309
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-text-muted", children: timeAgo(entry.timestamp) })
2310
- ] })
2311
- ] });
2312
- }
2313
-
2314
- // src/ai/components/PeerNetworkPanel.tsx
2315
- var import_react4 = require("react");
2316
- var import_jsx_runtime2 = require("react/jsx-runtime");
2317
- function timeAgo2(ts) {
2318
- const diff = Date.now() - ts;
2319
- if (diff < 6e4) return "just now";
2320
- if (diff < 36e5) return `${Math.floor(diff / 6e4)}m ago`;
2321
- return `${Math.floor(diff / 36e5)}h ago`;
2322
- }
2323
- function PeerNetworkPanel() {
2324
- const {
2325
- peers,
2326
- isHosting,
2327
- isConnected,
2328
- sharedResults,
2329
- peerCount,
2330
- startHost,
2331
- stopHost,
2332
- connectToPeer,
2333
- disconnect
2334
- } = usePeerMesh();
2335
- const [peerAddress, setPeerAddress] = (0, import_react4.useState)("");
2336
- const [hostPort, setHostPort] = (0, import_react4.useState)("9876");
2337
- const handleConnect = (0, import_react4.useCallback)(() => {
2338
- if (!peerAddress.trim()) return;
2339
- connectToPeer(peerAddress.trim());
2340
- setPeerAddress("");
2341
- }, [peerAddress, connectToPeer]);
2342
- const handleKeyDown = (0, import_react4.useCallback)((e) => {
2343
- if (e.key === "Enter") handleConnect();
2344
- }, [handleConnect]);
2345
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-3 p-4 bg-surface-secondary rounded-2xl border border-border-subtle", children: [
2346
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between", children: [
2347
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
2348
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-lg", children: "\u{1F310}" }),
2349
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "text-sm font-semibold text-text-primary", children: "Peer Network" }),
2350
- peerCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "px-1.5 py-0.5 text-[10px] font-bold rounded-full bg-emerald-500/20 text-accent-green", children: peerCount })
2351
- ] }),
2352
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StatusBadge, { isHosting, isConnected })
2353
- ] }),
2354
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-2 p-3 rounded-xl bg-surface-tertiary/50", children: [
2355
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between", children: [
2356
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col", children: [
2357
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-[11px] font-medium text-text-secondary", children: "Host a Group" }),
2358
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-[9px] text-text-muted", children: "Others can connect to you" })
2359
- ] }),
2360
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
2361
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2362
- "input",
2363
- {
2364
- className: "w-16 px-2 py-1 text-[11px] font-mono text-text-primary bg-surface-primary\n border border-border-subtle rounded-lg text-center\n focus:border-border-strong focus:outline-none transition-colors",
2365
- value: hostPort,
2366
- onChange: (e) => setHostPort(e.target.value),
2367
- placeholder: "Port",
2368
- disabled: isHosting
2369
- }
2370
- ),
2371
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2372
- "button",
2373
- {
2374
- onClick: () => isHosting ? stopHost() : startHost(Number(hostPort)),
2375
- className: `px-3 py-1 text-[11px] font-medium rounded-lg transition-all
2376
- ${isHosting ? "bg-red-500/15 text-accent-red hover:bg-red-500/25" : "bg-emerald-500/15 text-accent-green hover:bg-emerald-500/25"}`,
2377
- children: isHosting ? "\u23F9 Stop" : "\u25B6 Start"
2378
- }
2379
- )
2380
- ] })
2381
- ] }),
2382
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "h-px bg-border-subtle" }),
2383
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
2384
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-[11px] font-medium text-text-secondary", children: "Join a Group" }),
2385
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-2", children: [
2386
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2387
- "input",
2388
- {
2389
- className: "flex-1 px-3 py-1.5 text-[11px] font-mono text-text-primary bg-surface-primary\n border border-border-subtle rounded-lg placeholder:text-text-muted\n focus:border-border-strong focus:outline-none transition-colors",
2390
- value: peerAddress,
2391
- onChange: (e) => setPeerAddress(e.target.value),
2392
- onKeyDown: handleKeyDown,
2393
- placeholder: "192.168.1.10:9876",
2394
- disabled: isConnected
2395
- }
2396
- ),
2397
- isConnected ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2398
- "button",
2399
- {
2400
- onClick: disconnect,
2401
- className: "px-3 py-1.5 text-[11px] font-medium rounded-lg bg-red-500/15 text-accent-red\n hover:bg-red-500/25 transition-all",
2402
- children: "Disconnect"
2403
- }
2404
- ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2405
- "button",
2406
- {
2407
- onClick: handleConnect,
2408
- className: "px-3 py-1.5 text-[11px] font-medium rounded-lg bg-cyan-500/15 text-accent-cyan\n hover:bg-cyan-500/25 transition-all",
2409
- children: "Connect"
2410
- }
2411
- )
2412
- ] })
2413
- ] })
2414
- ] }),
2415
- peers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
2416
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-[10px] font-medium text-text-muted uppercase tracking-wider", children: "Connected Peers" }),
2417
- peers.map((peer) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PeerRow, { peer }, peer.id))
2418
- ] }),
2419
- !isHosting && !isConnected && peers.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center justify-center py-5 text-text-muted gap-1.5", children: [
2420
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-2xl opacity-40", children: "\u{1F517}" }),
2421
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-xs text-center", children: [
2422
- "Host a group or connect to a peer",
2423
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("br", {}),
2424
- "to share AI results"
2425
- ] })
2426
- ] }),
2427
- sharedResults.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-1.5 mt-1", children: [
2428
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-[10px] font-medium text-text-muted uppercase tracking-wider", children: [
2429
- "Shared Results (",
2430
- sharedResults.length,
2431
- ")"
2432
- ] }),
2433
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex flex-col gap-1 max-h-40 overflow-y-auto", children: sharedResults.slice(-8).reverse().map((r, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2434
- "div",
2435
- {
2436
- className: "px-3 py-2 rounded-xl bg-surface-glass text-[10px]",
2437
- children: [
2438
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between mb-1", children: [
2439
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-medium text-accent-purple", children: r.peerId.slice(0, 8) }),
2440
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-text-muted font-mono", children: r.model })
2441
- ] }),
2442
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "text-text-secondary line-clamp-2", children: [
2443
- r.response.slice(0, 120),
2444
- "..."
2445
- ] })
2446
- ]
2447
- },
2448
- `${r.timestamp}-${i}`
2449
- )) })
2450
- ] })
2451
- ] });
2452
- }
2453
- function StatusBadge({ isHosting, isConnected }) {
2454
- if (isHosting) {
2455
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-emerald-500/15 text-[10px] font-medium text-accent-green", children: [
2456
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse" }),
2457
- "Hosting"
2458
- ] });
2459
- }
2460
- if (isConnected) {
2461
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-cyan-500/15 text-[10px] font-medium text-accent-cyan", children: [
2462
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-cyan-400 animate-pulse" }),
2463
- "Connected"
2464
- ] });
2465
- }
2466
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-surface-glass text-[10px] text-text-muted", children: [
2467
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-surface-tertiary" }),
2468
- "Offline"
2469
- ] });
2470
- }
2471
- function PeerRow({ peer }) {
2472
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between px-3 py-2 rounded-xl bg-surface-glass transition-colors hover:bg-surface-tertiary/50", children: [
2473
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
2474
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-2 h-2 rounded-full bg-emerald-400" }),
2475
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-xs font-medium text-text-primary", children: peer.name })
2476
- ] }),
2477
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2 text-[10px] text-text-muted", children: [
2478
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-mono", children: peer.address }),
2479
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "\xB7" }),
2480
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: timeAgo2(peer.lastSeen) })
2481
- ] })
2482
- ] });
2483
- }
2484
-
2485
- // src/crypto.ts
2486
- async function decryptEvent(base64Payload, base64Key) {
2487
- try {
2488
- const keyBuffer = Uint8Array.from(atob(base64Key), (c) => c.charCodeAt(0));
2489
- const payloadBuffer = Uint8Array.from(atob(base64Payload), (c) => c.charCodeAt(0));
2490
- if (payloadBuffer.byteLength < 12) {
2491
- throw new Error("Payload too short to contain a valid nonce");
2492
- }
2493
- const nonce = payloadBuffer.slice(0, 12);
2494
- const ciphertext = payloadBuffer.slice(12);
2495
- const cryptoKey = await crypto.subtle.importKey(
2496
- "raw",
2497
- keyBuffer,
2498
- { name: "AES-GCM" },
2499
- false,
2500
- ["decrypt"]
2501
- );
2502
- const decryptedBuffer = await crypto.subtle.decrypt(
2503
- { name: "AES-GCM", iv: nonce },
2504
- cryptoKey,
2505
- ciphertext
2506
- );
2507
- const decoder = new TextDecoder();
2508
- const jsonString = decoder.decode(decryptedBuffer);
2509
- return JSON.parse(jsonString);
2510
- } catch (e) {
2511
- console.error("[Crypto Bridge] Failed to decrypt secure event:", e);
2512
- throw e;
2513
- }
2514
- }
2515
-
2516
- // src/rehydration.ts
2517
- var StateRehydrationManager = {
2518
- async rehydrate(swarmKey) {
2519
- console.log("[Rehydration] \u{1F504} Iniciando recuperaci\xF3n de estado...");
2520
- try {
2521
- const history = await kernel.execute("get_stream_history", { limit: 100 });
2522
- if (!Array.isArray(history)) {
2523
- console.warn("[Rehydration] El historial devuelto no es un array v\xE1lido.");
2524
- return;
2525
- }
2526
- const events = history.reverse();
2527
- for (const rawEvent of events) {
2528
- let event = rawEvent;
2529
- if (typeof rawEvent === "string") {
2530
- try {
2531
- event = await decryptEvent(rawEvent, swarmKey);
2532
- } catch (e) {
2533
- console.warn("[Rehydration] No se pudo descifrar un evento, saltando...");
2534
- continue;
2535
- }
2536
- }
2537
- this.replayEvent(event);
2538
- }
2539
- console.log(`[Rehydration] \u2705 Memoria restaurada: ${events.length} eventos procesados.`);
2540
- } catch (error) {
2541
- console.error("[Rehydration] Fallo cr\xEDtico en la rehidrataci\xF3n:", error);
2542
- }
2543
- },
2544
- replayEvent(event) {
2545
- try {
2546
- kernel.injectEvent({ ...event, is_rehydration: true });
2547
- } catch (e) {
2548
- console.warn("[Rehydration] Error inyectando evento de historial", e);
2549
- }
2550
- }
2551
- };
2552
- // Annotate the CommonJS export names for ESM import in node:
2553
- 0 && (module.exports = {
2554
- AnthropicProvider,
2555
- GeminiProvider,
2556
- OllamaProvider,
2557
- OpenAIProvider,
2558
- PeerNetworkPanel,
2559
- StateRehydrationManager,
2560
- TokenWalletPanel,
2561
- chat,
2562
- chatStream,
2563
- clearConversationHistory,
2564
- decryptEvent,
2565
- embeddingService,
2566
- getAllProviders,
2567
- getProviderStatuses,
2568
- inferenceRouter,
2569
- initProviders,
2570
- isOllamaAvailable,
2571
- kernel,
2572
- loadProviderKeys,
2573
- mlxBenchmarkModel,
2574
- mlxCompareModels,
2575
- mlxListModels,
2576
- mlxPullModel,
2577
- mlxRunInference,
2578
- ollamaChat,
2579
- ollamaChatStream,
2580
- ollamaListModels,
2581
- parseToolCalls,
2582
- peerMesh,
2583
- processLocalMessage,
2584
- routeChat,
2585
- routeInference,
2586
- saveProviderKeys,
2587
- setProviderApiKey,
2588
- startLoRATraining,
2589
- stripToolCalls,
2590
- tokenWallet,
2591
- usePeerMesh,
2592
- useTokenWallet
2593
- });
58
+ \u2022 "estado" \u2014 ver estado del sistema`}}var qe=require("uuid"),Ot=9876,Dt=1e4,je=3e4,We=8,de=class{myId=(0,qe.v4)();myName=this._getHostname();peers=new Map;connections=new Map;isHostingFlag=!1;isConnectedFlag=!1;heartbeatTimer=null;cleanupTimer=null;peersListeners=new Set;hostingListeners=new Set;connectionListeners=new Set;resultListeners=new Set;startHost(e=Ot){this.isHostingFlag||(this.isHostingFlag=!0,this._emitHosting(!0),this._startHeartbeat(),console.log(`\u{1F310} [PeerMesh] Hosting on port ${e} (peer: ${this.myId.slice(0,8)})`),console.log("\u{1F310} [PeerMesh] \u26A0\uFE0F Browser-mode: WebSocket server requires Tauri backend."),console.log("\u{1F310} [PeerMesh] Use connectToPeer() from another instance to connect."))}stopHost(){this.isHostingFlag&&(this.isHostingFlag=!1,this._stopHeartbeat(),this._broadcast({type:"leave",peerId:this.myId,peerName:this.myName}),this._disconnectAll(),this._emitHosting(!1),console.log("\u{1F310} [PeerMesh] Stopped hosting"))}connectToPeer(e){if(this.connections.size>=We){console.warn(`\u{1F310} [PeerMesh] Max peers (${We}) reached`);return}let t=e.startsWith("ws")?e:`ws://${e}`;console.log(`\u{1F310} [PeerMesh] Connecting to ${t}...`);try{let s=new WebSocket(t);s.onopen=()=>{console.log(`\u{1F310} [PeerMesh] \u2705 Connected to ${e}`),this.connections.set(e,s),this.isConnectedFlag=!0,this._emitConnection(!0),this._send(s,{type:"join",peerId:this.myId,peerName:this.myName}),this._startHeartbeat()},s.onmessage=r=>{try{let o=JSON.parse(r.data);this._handleMessage(o,e)}catch{}},s.onclose=()=>{console.log(`\u{1F310} [PeerMesh] Disconnected from ${e}`),this.connections.delete(e);for(let[r,o]of this.peers)o.address===e&&this.peers.delete(r);this._emitPeers(),this.connections.size===0&&(this.isConnectedFlag=!1,this._emitConnection(!1),this._stopHeartbeat())},s.onerror=r=>{console.error(`\u{1F310} [PeerMesh] Connection error to ${e}:`,r)}}catch(s){console.error("\u{1F310} [PeerMesh] Failed to connect:",s)}}disconnect(){this._broadcast({type:"leave",peerId:this.myId,peerName:this.myName}),this._disconnectAll()}shareResult(e){let t={...e,peerId:this.myId,timestamp:Date.now()};this._broadcast({type:"share-result",peerId:this.myId,peerName:this.myName,payload:t}),console.log(`\u{1F310} [PeerMesh] Shared result to ${this.connections.size} peers`)}onPeersChanged(e){return this.peersListeners.add(e),()=>{this.peersListeners.delete(e)}}onHostingChanged(e){return this.hostingListeners.add(e),()=>{this.hostingListeners.delete(e)}}onConnectionChanged(e){return this.connectionListeners.add(e),()=>{this.connectionListeners.delete(e)}}onSharedResult(e){return this.resultListeners.add(e),()=>{this.resultListeners.delete(e)}}getPeers(){return Array.from(this.peers.values())}getMyId(){return this.myId}getMyName(){return this.myName}_handleMessage(e,t){switch(e.type){case"join":this.peers.set(e.peerId,{id:e.peerId,name:e.peerName,address:t,joinedAt:Date.now(),lastSeen:Date.now()}),this._emitPeers(),console.log(`\u{1F310} [PeerMesh] Peer joined: ${e.peerName} (${e.peerId.slice(0,8)})`);break;case"leave":this.peers.delete(e.peerId),this._emitPeers(),console.log(`\u{1F310} [PeerMesh] Peer left: ${e.peerName}`);break;case"heartbeat":this.peers.has(e.peerId)&&(this.peers.get(e.peerId).lastSeen=Date.now());break;case"share-result":if(e.payload){let s=e.payload;for(let r of this.resultListeners)try{r(s)}catch{}}break;case"peers-update":if(Array.isArray(e.payload)){for(let s of e.payload)s.id!==this.myId&&!this.peers.has(s.id)&&this.peers.set(s.id,s);this._emitPeers()}break}}_send(e,t){e.readyState===WebSocket.OPEN&&e.send(JSON.stringify(t))}_broadcast(e){for(let t of this.connections.values())this._send(t,e)}_disconnectAll(){for(let e of this.connections.values())try{e.close()}catch{}this.connections.clear(),this.peers.clear(),this.isConnectedFlag=!1,this._stopHeartbeat(),this._emitPeers(),this._emitConnection(!1)}_startHeartbeat(){this.heartbeatTimer||(this.heartbeatTimer=setInterval(()=>{this._broadcast({type:"heartbeat",peerId:this.myId,peerName:this.myName})},Dt),this.cleanupTimer=setInterval(()=>{let e=Date.now(),t=!1;for(let[s,r]of this.peers)e-r.lastSeen>je&&(this.peers.delete(s),t=!0,console.log(`\u{1F310} [PeerMesh] Peer timed out: ${r.name}`));t&&this._emitPeers()},je))}_stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null),this.cleanupTimer&&(clearInterval(this.cleanupTimer),this.cleanupTimer=null)}_emitPeers(){let e=this.getPeers();for(let t of this.peersListeners)try{t(e)}catch{}}_emitHosting(e){for(let t of this.hostingListeners)try{t(e)}catch{}}_emitConnection(e){for(let t of this.connectionListeners)try{t(e)}catch{}}_getHostname(){try{return`user-${Math.random().toString(36).slice(2,6)}`}catch{return"anonymous"}}},b=new de;var C=require("react");function W(){let[n,e]=(0,C.useState)(P.getSummary());(0,C.useEffect)(()=>P.subscribe(e),[]);let t=(0,C.useCallback)(()=>{P.clearHistory()},[]),s=(0,C.useCallback)((r=20)=>P.getRecentHistory(r),[]);return{summary:n,clearHistory:t,getRecentHistory:s,totalTokens:n.totalTokens,totalCost:n.totalCostUsd,totalCalls:n.totalCalls,byProvider:n.byProvider}}var x=require("react");function q(){let[n,e]=(0,x.useState)([]),[t,s]=(0,x.useState)(!1),[r,o]=(0,x.useState)(!1),[a,i]=(0,x.useState)([]),d=(0,x.useRef)(a);d.current=a,(0,x.useEffect)(()=>{let p=b.onPeersChanged(w=>{e([...w])}),f=b.onHostingChanged(w=>{s(w)}),y=b.onConnectionChanged(w=>{o(w)}),N=b.onSharedResult(w=>{i(Ve=>[...Ve.slice(-49),w])});return()=>{p(),f(),y(),N()}},[]);let l=(0,x.useCallback)(p=>{b.startHost(p)},[]),h=(0,x.useCallback)(()=>{b.stopHost()},[]),m=(0,x.useCallback)(p=>{b.connectToPeer(p)},[]),g=(0,x.useCallback)(()=>{b.disconnect()},[]),v=(0,x.useCallback)(p=>{b.shareResult(p)},[]);return{peers:n,isHosting:t,isConnected:r,sharedResults:a,peerCount:n.length,startHost:l,stopHost:h,connectToPeer:m,disconnect:g,shareResult:v}}var ze=require("react");var u=require("react/jsx-runtime"),Bt={ollama:"text-accent-green",gemini:"text-accent-blue",anthropic:"text-accent-purple",openai:"text-accent-cyan",mlx:"text-accent-amber"},Ht={ollama:"bg-emerald-500/10",gemini:"bg-blue-500/10",anthropic:"bg-purple-500/10",openai:"bg-cyan-500/10",mlx:"bg-amber-500/10"},Ge={ollama:"\u{1F999}",gemini:"\u2728",anthropic:"\u{1F9E0}",openai:"\u26A1",mlx:"\u{1F34E}"};function me(n){return n===0?"FREE":n<.01?`$${n.toFixed(6)}`:n<1?`$${n.toFixed(4)}`:`$${n.toFixed(2)}`}function pe(n){return n>=1e6?`${(n/1e6).toFixed(1)}M`:n>=1e3?`${(n/1e3).toFixed(1)}K`:String(n)}function Ut(n){let e=Date.now()-n;return e<6e4?"just now":e<36e5?`${Math.floor(e/6e4)}m ago`:e<864e5?`${Math.floor(e/36e5)}h ago`:`${Math.floor(e/864e5)}d ago`}function Je(){let{summary:n,totalTokens:e,totalCost:t,totalCalls:s,byProvider:r,clearHistory:o,getRecentHistory:a}=W(),[i,d]=(0,ze.useState)(!1),l=Object.entries(r).sort((m,g)=>g[1].cost-m[1].cost),h=i?a(15):[];return(0,u.jsxs)("div",{className:"flex flex-col gap-3 p-4 bg-surface-secondary rounded-2xl border border-border-subtle",children:[(0,u.jsxs)("div",{className:"flex items-center justify-between",children:[(0,u.jsxs)("div",{className:"flex items-center gap-2",children:[(0,u.jsx)("span",{className:"text-lg",children:"\u{1F4B0}"}),(0,u.jsx)("h3",{className:"text-sm font-semibold text-text-primary",children:"Token Wallet"})]}),(0,u.jsx)("button",{onClick:o,className:`text-[10px] px-2 py-0.5 rounded-md bg-surface-glass text-text-muted
59
+ hover:bg-surface-tertiary hover:text-text-secondary transition-colors`,children:"Clear"})]}),(0,u.jsxs)("div",{className:"grid grid-cols-3 gap-2",children:[(0,u.jsx)(ue,{label:"Tokens",value:pe(e),accent:"text-accent-cyan"}),(0,u.jsx)(ue,{label:"Cost",value:me(t),accent:t===0?"text-accent-green":"text-accent-amber"}),(0,u.jsx)(ue,{label:"Calls",value:String(s),accent:"text-accent-blue"})]}),l.length>0&&(0,u.jsxs)("div",{className:"flex flex-col gap-1.5 mt-1",children:[(0,u.jsx)("span",{className:"text-[10px] font-medium text-text-muted uppercase tracking-wider",children:"By Provider"}),l.map(([m,g])=>(0,u.jsxs)("div",{className:`flex items-center justify-between px-3 py-2 rounded-xl ${Ht[m]||"bg-surface-glass"} transition-colors`,children:[(0,u.jsxs)("div",{className:"flex items-center gap-2",children:[(0,u.jsx)("span",{className:"text-sm",children:Ge[m]||"\u{1F916}"}),(0,u.jsx)("span",{className:`text-xs font-medium ${Bt[m]||"text-text-primary"}`,children:m.charAt(0).toUpperCase()+m.slice(1)})]}),(0,u.jsxs)("div",{className:"flex items-center gap-3 text-[11px]",children:[(0,u.jsxs)("span",{className:"text-text-muted",children:[g.calls," calls"]}),(0,u.jsx)("span",{className:"text-text-secondary font-mono",children:pe(g.tokens)}),(0,u.jsx)("span",{className:"font-semibold text-text-primary font-mono",children:me(g.cost)})]})]},m))]}),l.length===0&&(0,u.jsxs)("div",{className:"flex flex-col items-center justify-center py-6 text-text-muted gap-1",children:[(0,u.jsx)("span",{className:"text-2xl opacity-50",children:"\u{1F4CA}"}),(0,u.jsx)("span",{className:"text-xs",children:"No AI calls recorded yet"})]}),(0,u.jsx)("button",{onClick:()=>d(!i),className:"text-[10px] text-text-muted hover:text-text-secondary transition-colors self-center",children:i?"\u25B2 Hide recent":"\u25BC Show recent calls"}),i&&h.length>0&&(0,u.jsx)("div",{className:"flex flex-col gap-1 max-h-48 overflow-y-auto",children:h.reverse().map((m,g)=>(0,u.jsx)(Kt,{entry:m},`${m.timestamp}-${g}`))})]})}function ue({label:n,value:e,accent:t}){return(0,u.jsxs)("div",{className:"flex flex-col items-center p-2 rounded-xl bg-surface-tertiary/50",children:[(0,u.jsx)("span",{className:"text-[9px] text-text-muted uppercase tracking-wider",children:n}),(0,u.jsx)("span",{className:`text-base font-bold font-mono ${t}`,children:e})]})}function Kt({entry:n}){return(0,u.jsxs)("div",{className:"flex items-center justify-between px-2 py-1.5 rounded-lg bg-surface-glass text-[10px]",children:[(0,u.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,u.jsx)("span",{children:Ge[n.provider]||"\u{1F916}"}),(0,u.jsx)("span",{className:"text-text-secondary font-mono",children:n.model.split("/").pop()?.slice(0,18)})]}),(0,u.jsxs)("div",{className:"flex items-center gap-2",children:[(0,u.jsx)("span",{className:"text-text-muted",children:pe(n.totalTokens)}),(0,u.jsx)("span",{className:"text-text-primary font-mono",children:me(n.estimatedCostUsd)}),(0,u.jsx)("span",{className:"text-text-muted",children:Ut(n.timestamp)})]})]})}var I=require("react");var c=require("react/jsx-runtime");function Ft(n){let e=Date.now()-n;return e<6e4?"just now":e<36e5?`${Math.floor(e/6e4)}m ago`:`${Math.floor(e/36e5)}h ago`}function Xe(){let{peers:n,isHosting:e,isConnected:t,sharedResults:s,peerCount:r,startHost:o,stopHost:a,connectToPeer:i,disconnect:d}=q(),[l,h]=(0,I.useState)(""),[m,g]=(0,I.useState)("9876"),v=(0,I.useCallback)(()=>{l.trim()&&(i(l.trim()),h(""))},[l,i]),p=(0,I.useCallback)(f=>{f.key==="Enter"&&v()},[v]);return(0,c.jsxs)("div",{className:"flex flex-col gap-3 p-4 bg-surface-secondary rounded-2xl border border-border-subtle",children:[(0,c.jsxs)("div",{className:"flex items-center justify-between",children:[(0,c.jsxs)("div",{className:"flex items-center gap-2",children:[(0,c.jsx)("span",{className:"text-lg",children:"\u{1F310}"}),(0,c.jsx)("h3",{className:"text-sm font-semibold text-text-primary",children:"Peer Network"}),r>0&&(0,c.jsx)("span",{className:"px-1.5 py-0.5 text-[10px] font-bold rounded-full bg-emerald-500/20 text-accent-green",children:r})]}),(0,c.jsx)(jt,{isHosting:e,isConnected:t})]}),(0,c.jsxs)("div",{className:"flex flex-col gap-2 p-3 rounded-xl bg-surface-tertiary/50",children:[(0,c.jsxs)("div",{className:"flex items-center justify-between",children:[(0,c.jsxs)("div",{className:"flex flex-col",children:[(0,c.jsx)("span",{className:"text-[11px] font-medium text-text-secondary",children:"Host a Group"}),(0,c.jsx)("span",{className:"text-[9px] text-text-muted",children:"Others can connect to you"})]}),(0,c.jsxs)("div",{className:"flex items-center gap-2",children:[(0,c.jsx)("input",{className:`w-16 px-2 py-1 text-[11px] font-mono text-text-primary bg-surface-primary
60
+ border border-border-subtle rounded-lg text-center
61
+ focus:border-border-strong focus:outline-none transition-colors`,value:m,onChange:f=>g(f.target.value),placeholder:"Port",disabled:e}),(0,c.jsx)("button",{onClick:()=>e?a():o(Number(m)),className:`px-3 py-1 text-[11px] font-medium rounded-lg transition-all
62
+ ${e?"bg-red-500/15 text-accent-red hover:bg-red-500/25":"bg-emerald-500/15 text-accent-green hover:bg-emerald-500/25"}`,children:e?"\u23F9 Stop":"\u25B6 Start"})]})]}),(0,c.jsx)("div",{className:"h-px bg-border-subtle"}),(0,c.jsxs)("div",{className:"flex flex-col gap-1.5",children:[(0,c.jsx)("span",{className:"text-[11px] font-medium text-text-secondary",children:"Join a Group"}),(0,c.jsxs)("div",{className:"flex gap-2",children:[(0,c.jsx)("input",{className:`flex-1 px-3 py-1.5 text-[11px] font-mono text-text-primary bg-surface-primary
63
+ border border-border-subtle rounded-lg placeholder:text-text-muted
64
+ focus:border-border-strong focus:outline-none transition-colors`,value:l,onChange:f=>h(f.target.value),onKeyDown:p,placeholder:"192.168.1.10:9876",disabled:t}),t?(0,c.jsx)("button",{onClick:d,className:`px-3 py-1.5 text-[11px] font-medium rounded-lg bg-red-500/15 text-accent-red
65
+ hover:bg-red-500/25 transition-all`,children:"Disconnect"}):(0,c.jsx)("button",{onClick:v,className:`px-3 py-1.5 text-[11px] font-medium rounded-lg bg-cyan-500/15 text-accent-cyan
66
+ hover:bg-cyan-500/25 transition-all`,children:"Connect"})]})]})]}),n.length>0&&(0,c.jsxs)("div",{className:"flex flex-col gap-1.5",children:[(0,c.jsx)("span",{className:"text-[10px] font-medium text-text-muted uppercase tracking-wider",children:"Connected Peers"}),n.map(f=>(0,c.jsx)(Wt,{peer:f},f.id))]}),!e&&!t&&n.length===0&&(0,c.jsxs)("div",{className:"flex flex-col items-center justify-center py-5 text-text-muted gap-1.5",children:[(0,c.jsx)("span",{className:"text-2xl opacity-40",children:"\u{1F517}"}),(0,c.jsxs)("span",{className:"text-xs text-center",children:["Host a group or connect to a peer",(0,c.jsx)("br",{}),"to share AI results"]})]}),s.length>0&&(0,c.jsxs)("div",{className:"flex flex-col gap-1.5 mt-1",children:[(0,c.jsxs)("span",{className:"text-[10px] font-medium text-text-muted uppercase tracking-wider",children:["Shared Results (",s.length,")"]}),(0,c.jsx)("div",{className:"flex flex-col gap-1 max-h-40 overflow-y-auto",children:s.slice(-8).reverse().map((f,y)=>(0,c.jsxs)("div",{className:"px-3 py-2 rounded-xl bg-surface-glass text-[10px]",children:[(0,c.jsxs)("div",{className:"flex items-center justify-between mb-1",children:[(0,c.jsx)("span",{className:"font-medium text-accent-purple",children:f.peerId.slice(0,8)}),(0,c.jsx)("span",{className:"text-text-muted font-mono",children:f.model})]}),(0,c.jsxs)("p",{className:"text-text-secondary line-clamp-2",children:[f.response.slice(0,120),"..."]})]},`${f.timestamp}-${y}`))})]})]})}function jt({isHosting:n,isConnected:e}){return n?(0,c.jsxs)("span",{className:"flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-emerald-500/15 text-[10px] font-medium text-accent-green",children:[(0,c.jsx)("span",{className:"w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse"}),"Hosting"]}):e?(0,c.jsxs)("span",{className:"flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-cyan-500/15 text-[10px] font-medium text-accent-cyan",children:[(0,c.jsx)("span",{className:"w-1.5 h-1.5 rounded-full bg-cyan-400 animate-pulse"}),"Connected"]}):(0,c.jsxs)("span",{className:"flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-surface-glass text-[10px] text-text-muted",children:[(0,c.jsx)("span",{className:"w-1.5 h-1.5 rounded-full bg-surface-tertiary"}),"Offline"]})}function Wt({peer:n}){return(0,c.jsxs)("div",{className:"flex items-center justify-between px-3 py-2 rounded-xl bg-surface-glass transition-colors hover:bg-surface-tertiary/50",children:[(0,c.jsxs)("div",{className:"flex items-center gap-2",children:[(0,c.jsx)("span",{className:"w-2 h-2 rounded-full bg-emerald-400"}),(0,c.jsx)("span",{className:"text-xs font-medium text-text-primary",children:n.name})]}),(0,c.jsxs)("div",{className:"flex items-center gap-2 text-[10px] text-text-muted",children:[(0,c.jsx)("span",{className:"font-mono",children:n.address}),(0,c.jsx)("span",{children:"\xB7"}),(0,c.jsx)("span",{children:Ft(n.lastSeen)})]})]})}async function ge(n,e){try{let t=Uint8Array.from(atob(e),h=>h.charCodeAt(0)),s=Uint8Array.from(atob(n),h=>h.charCodeAt(0));if(s.byteLength<12)throw new Error("Payload too short to contain a valid nonce");let r=s.slice(0,12),o=s.slice(12),a=await crypto.subtle.importKey("raw",t,{name:"AES-GCM"},!1,["decrypt"]),i=await crypto.subtle.decrypt({name:"AES-GCM",iv:r},a,o),l=new TextDecoder().decode(i);return JSON.parse(l)}catch(t){throw console.error("[Crypto Bridge] Failed to decrypt secure event:",t),t}}var qt={async rehydrate(n){console.log("[Rehydration] \u{1F504} Iniciando recuperaci\xF3n de estado...");try{let e=await F.execute("get_stream_history",{limit:100});if(!Array.isArray(e)){console.warn("[Rehydration] El historial devuelto no es un array v\xE1lido.");return}let t=e.reverse();for(let s of t){let r=s;if(typeof s=="string")try{r=await ge(s,n)}catch{console.warn("[Rehydration] No se pudo descifrar un evento, saltando...");continue}this.replayEvent(r)}console.log(`[Rehydration] \u2705 Memoria restaurada: ${t.length} eventos procesados.`)}catch(e){console.error("[Rehydration] Fallo cr\xEDtico en la rehidrataci\xF3n:",e)}},replayEvent(n){try{F.injectEvent({...n,is_rehydration:!0})}catch(e){console.warn("[Rehydration] Error inyectando evento de historial",e)}}};0&&(module.exports={AnthropicProvider,GeminiProvider,OllamaProvider,OpenAIProvider,PeerNetworkPanel,StateRehydrationManager,TokenWalletPanel,chat,chatStream,clearConversationHistory,decryptEvent,embeddingService,getAllProviders,getProviderStatuses,inferenceRouter,initProviders,isOllamaAvailable,kernel,loadProviderKeys,mlxBenchmarkModel,mlxCompareModels,mlxListModels,mlxPullModel,mlxRunInference,ollamaChat,ollamaChatStream,ollamaListModels,parseToolCalls,peerMesh,processLocalMessage,routeChat,routeInference,saveProviderKeys,setProviderApiKey,startLoRATraining,stripToolCalls,tokenWallet,usePeerMesh,useTokenWallet});