@holaboss/client 0.2.0-beta.3 → 0.2.0-beta.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.
- package/dist/index.cjs +5 -5
- package/dist/index.d.cts +8 -5
- package/dist/index.d.ts +8 -5
- package/dist/index.js +5 -5
- package/package.json +5 -3
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
var F=Object.defineProperty;var
|
|
3
|
-
`))s.startsWith(":")||(s.startsWith("event:")?t=s.slice(6).trim():s.startsWith("data:")&&a.push(s.slice(5).replace(
|
|
4
|
-
`)}:null},
|
|
2
|
+
var F=Object.defineProperty;var X=Object.getOwnPropertyDescriptor;var Z=Object.getOwnPropertyNames;var ee=Object.prototype.hasOwnProperty;var te=(e,t)=>{for(var a in t)F(e,a,{get:t[a],enumerable:!0})},re=(e,t,a,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Z(t))!ee.call(e,n)&&n!==a&&F(e,n,{get:()=>t[n],enumerable:!(s=X(t,n))||s.enumerable});return e};var oe=e=>re(F({},"__esModule",{value:!0}),e);var ue={};te(ue,{Chat:()=>B,HolaProvider:()=>q,PortalConflict:()=>v,createApiTransport:()=>z,createHolaClient:()=>K,createPortalData:()=>V,createWidgetTransport:()=>O,mergeMessage:()=>L,textDeltaOf:()=>I,thinkingDeltaOf:()=>R,toolEventOf:()=>E,useArtifactUrl:()=>k,useChat:()=>U,useHolaClient:()=>A,useThread:()=>Q});module.exports=oe(ue);var W=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="message_update"?t.assistantMessageEvent??null:null},I=e=>{let t=W(e);return t?.type==="text_delta"&&typeof t.delta=="string"?t.delta:null},R=e=>{let t=W(e);return t?.type==="thinking_delta"&&typeof t.delta=="string"?t.delta:null},E=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="tool_execution_start"&&t.toolCallId?{phase:"start",id:t.toolCallId,name:t.toolName}:t.type==="tool_execution_end"&&t.toolCallId?{phase:"end",id:t.toolCallId,isError:t.isError}:null};var se=/^ /,C=e=>{if(e&&typeof e=="object"){let t=e,a=typeof t.error=="string"?t.error:"request_failed";return typeof t.retryable=="boolean"?{error:a,retryable:t.retryable}:{error:a}}return{error:"request_failed"}},ne=e=>{let t="message",a=[];for(let s of e.split(`
|
|
3
|
+
`))s.startsWith(":")||(s.startsWith("event:")?t=s.slice(6).trim():s.startsWith("data:")&&a.push(s.slice(5).replace(se,"")));return a.length>0?{event:t,data:a.join(`
|
|
4
|
+
`)}:null},ae=(e,t,a)=>{let s;try{s=JSON.parse(e.data)}catch{return}if(e.event==="session"){let n=s.sessionId;n&&(a(n),t.onSession?.(n));return}if(e.event==="turn_event"){t.onEvent?.(s);let n=I(s);n&&t.onText?.(n);let d=R(s);d&&t.onThinking?.(d);let r=E(s);r&&t.onTool?.(r);return}if(e.event==="chat_completed"){let n=s;a(n.sessionId),t.onComplete?.(n);return}e.event==="chat_error"&&t.onError?.(C(s))},h=async(e,t)=>{let a=await fetch(e,{headers:t});if(!a.ok)throw new Error(`hola: request failed (${a.status})`);return await a.json()},j=async e=>{let t=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body});if(!t.ok)throw new Error(`hola: message failed (${t.status})`);return await t.json()},N=async(e,t,a,s)=>{let n=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body,signal:s});if(!(n.ok&&n.body)){t.onError?.({error:`stream_failed_${n.status}`});return}let d=n.body.getReader(),r=new TextDecoder,o="",l=()=>{let c=o.indexOf(`
|
|
5
5
|
|
|
6
|
-
`);for(;c!==-1;){let b=o.slice(0,c);o=o.slice(c+2);let u=
|
|
6
|
+
`);for(;c!==-1;){let b=o.slice(0,c);o=o.slice(c+2);let u=ne(b);u&&ae(u,t,a),c=o.indexOf(`
|
|
7
7
|
|
|
8
|
-
`)}},i=async()=>{let c=await d.read();if(!c.done)return o+=r.decode(c.value,{stream:!0}),l(),i()};await i()};var
|
|
8
|
+
`)}},i=async()=>{let c=await d.read();if(!c.done)return o+=r.decode(c.value,{stream:!0}),l(),i()};await i()};var ie=/\/+$/,S="/api/v1/end-user",z=e=>{let t=e.baseUrl.replace(ie,""),a=()=>typeof e.token=="function"?e.token():e.token,s=r=>e.project?`${r}project=${encodeURIComponent(e.project)}`:"",n=async()=>({"content-type":"application/json",authorization:`Bearer ${await a()}`}),d=async r=>{let o=await fetch(`${t}${S}/chat${s("?")}`,{method:"POST",headers:await n(),body:JSON.stringify({message:r})});if(!o.ok)throw new Error(`hola: message failed (${o.status})`);return{reply:(await o.json()).reply??"",sessionId:"",artifacts:[]}};return{sessionId:()=>"",getInfo:async()=>h(`${t}${S}/chat/info${s("?")}`,await n()),sendMessage:d,streamMessage:async(r,o)=>{try{let l=await d(r);o.onText?.(l.reply),o.onComplete?.(l)}catch(l){o.onError?.(C(l))}},subscribe:(r,o)=>{let l=o?.since??"0",i=!1,c,b=o?.intervalMs??3e3,u=async()=>{try{let g=await h(`${t}${S}/chat/thread?since=${encodeURIComponent(l)}${s("&")}`,await n());for(let m of g.messages)r.onMessage(m);l=g.cursor}catch(g){r.onError?.(C(g))}i||(c=setTimeout(u,b))},p=()=>{i=!0,c&&clearTimeout(c)};return o?.signal?.addEventListener("abort",p),c=setTimeout(u,0),p},requestHuman:async()=>{await fetch(`${t}${S}/chat/request-human${s("?")}`,{method:"POST",headers:await n(),body:"{}"})}}};var $=require("react"),k=(e,t)=>{let[a,s]=(0,$.useState)("");return(0,$.useEffect)(()=>{if(!e){s("");return}let n=Uint8Array.from(atob(e),r=>r.charCodeAt(0)),d=URL.createObjectURL(new Blob([n],{type:t}));return s(d),()=>URL.revokeObjectURL(d)},[e,t]),a};var D=require("react");var M=require("react"),G=require("react/jsx-runtime"),J=(0,M.createContext)(null),q=({client:e,children:t})=>(0,G.jsx)(J.Provider,{value:e,children:t}),A=()=>{let e=(0,M.useContext)(J);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};var w=require("react"),U=e=>{let[t,a]=(0,w.useState)([]),[s,n]=(0,w.useState)(""),[d,r]=(0,w.useState)(!1),[o,l]=(0,w.useState)(null),i=(0,w.useRef)(0),c=(0,w.useCallback)(()=>{let u=i.current;return i.current+=1,u},[]);(0,w.useEffect)(()=>{let u=!0;return e.getInfo().then(p=>{u&&p.greeting&&a([{id:c(),role:"assistant",text:p.greeting}])}).catch(()=>{}),()=>{u=!1}},[e,c]);let b=(0,w.useCallback)(()=>{let u=s.trim();if(!u||d)return;n(""),l(null),r(!0);let p=c(),g=c();a(f=>[...f,{id:p,role:"user",text:u},{id:g,role:"assistant",text:"",streaming:!0}]);let m=f=>{a(H=>H.map(_=>_.id===g?f(_):_))};e.streamMessage(u,{onText:f=>m(H=>({...H,text:H.text+f})),onComplete:f=>{m(H=>({...H,text:f.reply||H.text,artifacts:f.artifacts,streaming:!1})),r(!1)},onError:f=>{m(H=>({...H,streaming:!1})),l(f.error),r(!1)}}).catch(f=>{m(H=>({...H,streaming:!1})),l(f instanceof Error?f.message:"stream_failed"),r(!1)})},[s,d,e,c]);return{messages:t,input:s,setInput:n,send:b,sending:d,error:o}};var y=require("react/jsx-runtime"),T={root:{display:"flex",flexDirection:"column",height:"100%",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:14,color:"#111827"},log:{flex:1,overflowY:"auto",padding:12,display:"flex",flexDirection:"column",gap:8},user:{alignSelf:"flex-end",background:"#111827",color:"#ffffff",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},assistant:{alignSelf:"flex-start",background:"#f1f1f4",color:"#111827",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},artifacts:{display:"flex",flexDirection:"column",gap:6,marginTop:6},image:{maxHeight:220,borderRadius:8,border:"1px solid #e5e7eb"},link:{color:"#2563eb",textDecoration:"underline",wordBreak:"break-all"},error:{color:"#b91c1c",padding:"4px 12px",fontSize:13},form:{display:"flex",gap:8,borderTop:"1px solid #e5e7eb",padding:8},input:{flex:1,border:"1px solid #d1d5db",borderRadius:8,padding:"8px 10px",fontSize:14,outline:"none"},button:{border:"none",background:"#111827",color:"#ffffff",borderRadius:8,padding:"8px 14px",fontSize:14,cursor:"pointer"}},le=({artifact:e})=>{let t=k(e.dataBase64??"",e.mimeType),a=e.url??t;return a?e.mimeType.startsWith("image/")?(0,y.jsx)("a",{href:a,rel:"noopener noreferrer",target:"_blank",children:(0,y.jsx)("img",{alt:e.filename,src:a,style:T.image})}):(0,y.jsx)("a",{download:e.filename,href:a,style:T.link,children:e.filename}):null},B=()=>{let e=A(),{messages:t,input:a,setInput:s,send:n,sending:d,error:r}=U(e),o=(0,D.useRef)(null);(0,D.useEffect)(()=>{let i=o.current;i&&(i.scrollTop=i.scrollHeight)},[t]);let l=i=>{i.preventDefault(),n()};return(0,y.jsxs)("div",{style:T.root,children:[(0,y.jsx)("div",{ref:o,style:T.log,children:t.map(i=>(0,y.jsxs)("div",{style:i.role==="user"?T.user:T.assistant,children:[i.text||(i.streaming?"\u2026":""),i.artifacts&&i.artifacts.length>0?(0,y.jsx)("div",{style:T.artifacts,children:i.artifacts.map(c=>(0,y.jsx)(le,{artifact:c},c.filename))}):null]},i.id))}),r?(0,y.jsx)("div",{style:T.error,children:r}):null,(0,y.jsxs)("form",{onSubmit:l,style:T.form,children:[(0,y.jsx)("input",{"aria-label":"Message",onChange:i=>s(i.target.value),placeholder:"Type a message\u2026",style:T.input,value:a}),(0,y.jsx)("button",{disabled:d||a.trim().length===0,style:T.button,type:"submit",children:"Send"})]})]})};var ce=/\/+$/,O=e=>{let t=e.sessionId,a=e.baseUrl.replace(ce,""),s=r=>`${a}/endpoint/${encodeURIComponent(e.token)}${r}`,n={"content-type":"application/json"},d=(r,o)=>({url:s(r),headers:n,body:JSON.stringify({message:o,...t?{sessionId:t}:{},...e.identity?{externalUserId:e.identity.externalUserId,userHmac:e.identity.userHmac}:{}})});return{sessionId:()=>t,getInfo:()=>h(s("/config"),{}),sendMessage:async r=>{let o=await j(d("/message",r));return t=o.sessionId,o},streamMessage:(r,o,l)=>N(d("/message/stream",r),o,i=>{t=i},l),subscribe:(r,o)=>{let l=o?.since??"0",i=!1,c,b=o?.intervalMs??3e3,u=async()=>{let g=t;if(g)try{let m=await h(s(`/messages?session=${encodeURIComponent(g)}&since=${encodeURIComponent(l)}`),{});for(let f of m.messages)r.onMessage(f);l=m.cursor}catch(m){r.onError?.(C(m))}i||(c=setTimeout(u,b))},p=()=>{i=!0,c&&clearTimeout(c)};return o?.signal?.addEventListener("abort",p),c=setTimeout(u,0),p},requestHuman:async()=>{t&&await fetch(s("/request-human"),{method:"POST",headers:n,body:JSON.stringify({sessionId:t})})}}};var K=e=>"transport"in e?e.transport:O(e);var de=/\/+$/,P="/api/v1/end-user",v=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function pe(e){let t="";for(let s=0;s<e.length;s+=32768)t+=String.fromCharCode(...e.subarray(s,s+32768));return btoa(t)}var V=e=>{let t=e.baseUrl.replace(de,""),a=()=>typeof e.token=="function"?e.token():e.token,s=async()=>({"content-type":"application/json",authorization:`Bearer ${await a()}`}),n=async(r,o)=>{try{let l=await fetch(`${t}${P}${r}`,{method:o.method,headers:await s(),...o.body===void 0?{}:{body:JSON.stringify(o.body)}});if(l.status===409){let i=await l.json().catch(()=>({}));throw new v(i.message??"Someone else saved this first.")}if(!l.ok){let i=await l.text().catch(()=>""),c=`${o.method} ${r} \u2192 ${String(l.status)}`;try{let b=JSON.parse(i);b.message&&(c=b.message)}catch{c=i?`${c}: ${i}`:c}throw C(new Error(c))}return await l.json()}catch(l){throw C(l)}},d=r=>`/tables/${encodeURIComponent(r)}`;return{listTables:async()=>(await h(`${t}${P}/tables`,await s())).tables,listRows:async(r,o)=>await h(`${t}${P}${d(r)}/rows?limit=${String(o??100)}`,await s()),insertRow:async(r,o)=>{let{rowIds:l}=await n(`${d(r)}/rows`,{method:"POST",body:{rows:[o]}}),i=l[0];if(!i)throw C(new Error("The row was not created."));return i},patchRow:async(r,o,l)=>(await n(`${d(r)}/rows/${encodeURIComponent(o)}`,{method:"PATCH",body:l})).updated,deleteRow:async(r,o)=>(await n(`/${encodeURIComponent(r)}/${encodeURIComponent(o)}`,{method:"DELETE"})).deleted,listFiles:async()=>(await h(`${t}${P}/tables/files/rows?limit=200`,await s())).rows.map(o=>({id:o.rowId,filename:String(o.data.filename??""),source:String(o.data.source??"client"),campaign:String(o.data.campaign??""),size:typeof o.data.size=="number"?o.data.size:0,createdAt:o.createdAt})),uploadFile:async(r,o)=>{let l=new Uint8Array(await r.arrayBuffer());return await n("/files",{method:"POST",body:{filename:r.name,mime:r.type||"application/octet-stream",data:pe(l),...o===void 0?{}:{message:o}}})},fileLink:async r=>await h(`${t}${P}/files/${encodeURIComponent(r)}/url`,await s()),deleteFile:async r=>(await n(`/files/${encodeURIComponent(r)}`,{method:"DELETE"})).deleted,listDocuments:async()=>(await h(`${t}${P}/documents`,await s())).documents,readDocument:async r=>(await h(`${t}${P}/documents/${encodeURIComponent(r)}`,await s())).document,saveDocument:async(r,o)=>await n(`/documents/${encodeURIComponent(r)}`,{method:"PATCH",body:o})}};var x=require("react"),Y="greeting";function L(e,t){if(e.some(n=>n.id===t.id))return e;let a=e.filter(n=>n.id!==Y);return[...t.author==="client"?a.filter(n=>!(n.pending&&n.author==="client"&&n.text===t.text)):a,t]}var Q=(e,t)=>{let[a,s]=(0,x.useState)([]),[n,d]=(0,x.useState)(""),[r,o]=(0,x.useState)(!1),[l,i]=(0,x.useState)(null),c=(0,x.useRef)(0),b=t?.pollMs??2e3;(0,x.useEffect)(()=>{let p=!0;return e.getInfo().then(g=>{p&&g.greeting&&s(m=>m.length===0?[{id:Y,cursor:"",author:"agent",text:g.greeting}]:m)}).catch(()=>{}),()=>{p=!1}},[e]),(0,x.useEffect)(()=>e.subscribe?.({onMessage:p=>s(g=>L(g,p)),onError:p=>i(p.error)},{intervalMs:b}),[e,b]);let u=(0,x.useCallback)(()=>{let p=n.trim();if(!p||r)return;d(""),i(null),o(!0),c.current+=1;let g={id:`pending-${c.current}`,cursor:"",author:"client",text:p,pending:!0};s(m=>[...m,g]),e.sendMessage(p).then(()=>o(!1)).catch(m=>{i(m instanceof Error?m.message:"send_failed"),o(!1)})},[n,r,e]);return{messages:a,input:n,setInput:d,send:u,sending:r,error:l}};0&&(module.exports={Chat,HolaProvider,PortalConflict,createApiTransport,createHolaClient,createPortalData,createWidgetTransport,mergeMessage,textDeltaOf,thinkingDeltaOf,toolEventOf,useArtifactUrl,useChat,useHolaClient,useThread});
|
package/dist/index.d.cts
CHANGED
|
@@ -244,8 +244,14 @@ type PortalData = {
|
|
|
244
244
|
insertRow(slug: string, row: Record<string, unknown>): Promise<string>;
|
|
245
245
|
/** Pass an empty string to clear a field. */
|
|
246
246
|
patchRow(slug: string, rowId: string, patch: Record<string, unknown>): Promise<number>;
|
|
247
|
-
/**
|
|
248
|
-
*
|
|
247
|
+
/**
|
|
248
|
+
* Only what the company allows a customer to delete, and only when nothing
|
|
249
|
+
* depends on it. Rejects with the reason otherwise.
|
|
250
|
+
*
|
|
251
|
+
* Genuinely any table: which ones are deletable, and what holds a row back, is
|
|
252
|
+
* declared in the company's own schema rather than known to the platform. This
|
|
253
|
+
* used to look general and answer for two hard-coded slugs.
|
|
254
|
+
*/
|
|
249
255
|
deleteRow(slug: string, rowId: string): Promise<number>;
|
|
250
256
|
/** Everything exchanged with this customer, newest first. */
|
|
251
257
|
listFiles(): Promise<PortalFile[]>;
|
|
@@ -327,9 +333,6 @@ type UseThread = {
|
|
|
327
333
|
sending: boolean;
|
|
328
334
|
error: string | null;
|
|
329
335
|
};
|
|
330
|
-
/** Merge one incoming thread message: dedupe by id, and replace a pending optimistic echo of the
|
|
331
|
-
* visitor's own send (same text) with the authoritative one. Pure — the hook's core, so the
|
|
332
|
-
* merge rule is unit-checkable without React. */
|
|
333
336
|
declare function mergeMessage(list: ThreadMessage[], incoming: HolaMessage): ThreadMessage[];
|
|
334
337
|
/** A poll-based UNIFIED thread: the whole conversation — visitor, agent, and HUMAN turns — as one
|
|
335
338
|
* live list via the receive channel (`client.subscribe`). Unlike `useChat` (streaming, one turn
|
package/dist/index.d.ts
CHANGED
|
@@ -244,8 +244,14 @@ type PortalData = {
|
|
|
244
244
|
insertRow(slug: string, row: Record<string, unknown>): Promise<string>;
|
|
245
245
|
/** Pass an empty string to clear a field. */
|
|
246
246
|
patchRow(slug: string, rowId: string, patch: Record<string, unknown>): Promise<number>;
|
|
247
|
-
/**
|
|
248
|
-
*
|
|
247
|
+
/**
|
|
248
|
+
* Only what the company allows a customer to delete, and only when nothing
|
|
249
|
+
* depends on it. Rejects with the reason otherwise.
|
|
250
|
+
*
|
|
251
|
+
* Genuinely any table: which ones are deletable, and what holds a row back, is
|
|
252
|
+
* declared in the company's own schema rather than known to the platform. This
|
|
253
|
+
* used to look general and answer for two hard-coded slugs.
|
|
254
|
+
*/
|
|
249
255
|
deleteRow(slug: string, rowId: string): Promise<number>;
|
|
250
256
|
/** Everything exchanged with this customer, newest first. */
|
|
251
257
|
listFiles(): Promise<PortalFile[]>;
|
|
@@ -327,9 +333,6 @@ type UseThread = {
|
|
|
327
333
|
sending: boolean;
|
|
328
334
|
error: string | null;
|
|
329
335
|
};
|
|
330
|
-
/** Merge one incoming thread message: dedupe by id, and replace a pending optimistic echo of the
|
|
331
|
-
* visitor's own send (same text) with the authoritative one. Pure — the hook's core, so the
|
|
332
|
-
* merge rule is unit-checkable without React. */
|
|
333
336
|
declare function mergeMessage(list: ThreadMessage[], incoming: HolaMessage): ThreadMessage[];
|
|
334
337
|
/** A poll-based UNIFIED thread: the whole conversation — visitor, agent, and HUMAN turns — as one
|
|
335
338
|
* live list via the receive channel (`client.subscribe`). Unlike `useChat` (streaming, one turn
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
var O=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="message_update"?t.assistantMessageEvent??null:null},E=e=>{let t=O(e);return t?.type==="text_delta"&&typeof t.delta=="string"?t.delta:null},S=e=>{let t=O(e);return t?.type==="thinking_delta"&&typeof t.delta=="string"?t.delta:null},$=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="tool_execution_start"&&t.toolCallId?{phase:"start",id:t.toolCallId,name:t.toolName}:t.type==="tool_execution_end"&&t.toolCallId?{phase:"end",id:t.toolCallId,isError:t.isError}:null};var
|
|
3
|
-
`))s.startsWith(":")||(s.startsWith("event:")?t=s.slice(6).trim():s.startsWith("data:")&&i.push(s.slice(5).replace(
|
|
4
|
-
`)}:null},
|
|
2
|
+
var O=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="message_update"?t.assistantMessageEvent??null:null},E=e=>{let t=O(e);return t?.type==="text_delta"&&typeof t.delta=="string"?t.delta:null},S=e=>{let t=O(e);return t?.type==="thinking_delta"&&typeof t.delta=="string"?t.delta:null},$=e=>{if(!e||typeof e!="object")return null;let t=e;return t.type==="tool_execution_start"&&t.toolCallId?{phase:"start",id:t.toolCallId,name:t.toolName}:t.type==="tool_execution_end"&&t.toolCallId?{phase:"end",id:t.toolCallId,isError:t.isError}:null};var J=/^ /,x=e=>{if(e&&typeof e=="object"){let t=e,i=typeof t.error=="string"?t.error:"request_failed";return typeof t.retryable=="boolean"?{error:i,retryable:t.retryable}:{error:i}}return{error:"request_failed"}},q=e=>{let t="message",i=[];for(let s of e.split(`
|
|
3
|
+
`))s.startsWith(":")||(s.startsWith("event:")?t=s.slice(6).trim():s.startsWith("data:")&&i.push(s.slice(5).replace(J,"")));return i.length>0?{event:t,data:i.join(`
|
|
4
|
+
`)}:null},G=(e,t,i)=>{let s;try{s=JSON.parse(e.data)}catch{return}if(e.event==="session"){let n=s.sessionId;n&&(i(n),t.onSession?.(n));return}if(e.event==="turn_event"){t.onEvent?.(s);let n=E(s);n&&t.onText?.(n);let d=S(s);d&&t.onThinking?.(d);let r=$(s);r&&t.onTool?.(r);return}if(e.event==="chat_completed"){let n=s;i(n.sessionId),t.onComplete?.(n);return}e.event==="chat_error"&&t.onError?.(x(s))},b=async(e,t)=>{let i=await fetch(e,{headers:t});if(!i.ok)throw new Error(`hola: request failed (${i.status})`);return await i.json()},_=async e=>{let t=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body});if(!t.ok)throw new Error(`hola: message failed (${t.status})`);return await t.json()},F=async(e,t,i,s)=>{let n=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body,signal:s});if(!(n.ok&&n.body)){t.onError?.({error:`stream_failed_${n.status}`});return}let d=n.body.getReader(),r=new TextDecoder,o="",l=()=>{let c=o.indexOf(`
|
|
5
5
|
|
|
6
|
-
`);for(;c!==-1;){let y=o.slice(0,c);o=o.slice(c+2);let u=
|
|
6
|
+
`);for(;c!==-1;){let y=o.slice(0,c);o=o.slice(c+2);let u=q(y);u&&G(u,t,i),c=o.indexOf(`
|
|
7
7
|
|
|
8
|
-
`)}},
|
|
8
|
+
`)}},a=async()=>{let c=await d.read();if(!c.done)return o+=r.decode(c.value,{stream:!0}),l(),a()};await a()};var B=/\/+$/,C="/api/v1/end-user",K=e=>{let t=e.baseUrl.replace(B,""),i=()=>typeof e.token=="function"?e.token():e.token,s=r=>e.project?`${r}project=${encodeURIComponent(e.project)}`:"",n=async()=>({"content-type":"application/json",authorization:`Bearer ${await i()}`}),d=async r=>{let o=await fetch(`${t}${C}/chat${s("?")}`,{method:"POST",headers:await n(),body:JSON.stringify({message:r})});if(!o.ok)throw new Error(`hola: message failed (${o.status})`);return{reply:(await o.json()).reply??"",sessionId:"",artifacts:[]}};return{sessionId:()=>"",getInfo:async()=>b(`${t}${C}/chat/info${s("?")}`,await n()),sendMessage:d,streamMessage:async(r,o)=>{try{let l=await d(r);o.onText?.(l.reply),o.onComplete?.(l)}catch(l){o.onError?.(x(l))}},subscribe:(r,o)=>{let l=o?.since??"0",a=!1,c,y=o?.intervalMs??3e3,u=async()=>{try{let g=await b(`${t}${C}/chat/thread?since=${encodeURIComponent(l)}${s("&")}`,await n());for(let m of g.messages)r.onMessage(m);l=g.cursor}catch(g){r.onError?.(x(g))}a||(c=setTimeout(u,y))},p=()=>{a=!0,c&&clearTimeout(c)};return o?.signal?.addEventListener("abort",p),c=setTimeout(u,0),p},requestHuman:async()=>{await fetch(`${t}${C}/chat/request-human${s("?")}`,{method:"POST",headers:await n(),body:"{}"})}}};import{useEffect as V,useState as Y}from"react";var k=(e,t)=>{let[i,s]=Y("");return V(()=>{if(!e){s("");return}let n=Uint8Array.from(atob(e),r=>r.charCodeAt(0)),d=URL.createObjectURL(new Blob([n],{type:t}));return s(d),()=>URL.revokeObjectURL(d)},[e,t]),i};import{useEffect as oe,useRef as se}from"react";import{createContext as Q,useContext as X}from"react";import{jsx as ee}from"react/jsx-runtime";var L=Q(null),Z=({client:e,children:t})=>ee(L.Provider,{value:e,children:t}),M=()=>{let e=X(L);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};import{useCallback as W,useEffect as te,useRef as re,useState as P}from"react";var A=e=>{let[t,i]=P([]),[s,n]=P(""),[d,r]=P(!1),[o,l]=P(null),a=re(0),c=W(()=>{let u=a.current;return a.current+=1,u},[]);te(()=>{let u=!0;return e.getInfo().then(p=>{u&&p.greeting&&i([{id:c(),role:"assistant",text:p.greeting}])}).catch(()=>{}),()=>{u=!1}},[e,c]);let y=W(()=>{let u=s.trim();if(!u||d)return;n(""),l(null),r(!0);let p=c(),g=c();i(f=>[...f,{id:p,role:"user",text:u},{id:g,role:"assistant",text:"",streaming:!0}]);let m=f=>{i(h=>h.map(R=>R.id===g?f(R):R))};e.streamMessage(u,{onText:f=>m(h=>({...h,text:h.text+f})),onComplete:f=>{m(h=>({...h,text:f.reply||h.text,artifacts:f.artifacts,streaming:!1})),r(!1)},onError:f=>{m(h=>({...h,streaming:!1})),l(f.error),r(!1)}}).catch(f=>{m(h=>({...h,streaming:!1})),l(f instanceof Error?f.message:"stream_failed"),r(!1)})},[s,d,e,c]);return{messages:t,input:s,setInput:n,send:y,sending:d,error:o}};import{jsx as H,jsxs as U}from"react/jsx-runtime";var w={root:{display:"flex",flexDirection:"column",height:"100%",fontFamily:"system-ui, -apple-system, sans-serif",fontSize:14,color:"#111827"},log:{flex:1,overflowY:"auto",padding:12,display:"flex",flexDirection:"column",gap:8},user:{alignSelf:"flex-end",background:"#111827",color:"#ffffff",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},assistant:{alignSelf:"flex-start",background:"#f1f1f4",color:"#111827",borderRadius:12,padding:"8px 12px",maxWidth:"80%",whiteSpace:"pre-wrap"},artifacts:{display:"flex",flexDirection:"column",gap:6,marginTop:6},image:{maxHeight:220,borderRadius:8,border:"1px solid #e5e7eb"},link:{color:"#2563eb",textDecoration:"underline",wordBreak:"break-all"},error:{color:"#b91c1c",padding:"4px 12px",fontSize:13},form:{display:"flex",gap:8,borderTop:"1px solid #e5e7eb",padding:8},input:{flex:1,border:"1px solid #d1d5db",borderRadius:8,padding:"8px 10px",fontSize:14,outline:"none"},button:{border:"none",background:"#111827",color:"#ffffff",borderRadius:8,padding:"8px 14px",fontSize:14,cursor:"pointer"}},ne=({artifact:e})=>{let t=k(e.dataBase64??"",e.mimeType),i=e.url??t;return i?e.mimeType.startsWith("image/")?H("a",{href:i,rel:"noopener noreferrer",target:"_blank",children:H("img",{alt:e.filename,src:i,style:w.image})}):H("a",{download:e.filename,href:i,style:w.link,children:e.filename}):null},ae=()=>{let e=M(),{messages:t,input:i,setInput:s,send:n,sending:d,error:r}=A(e),o=se(null);oe(()=>{let a=o.current;a&&(a.scrollTop=a.scrollHeight)},[t]);let l=a=>{a.preventDefault(),n()};return U("div",{style:w.root,children:[H("div",{ref:o,style:w.log,children:t.map(a=>U("div",{style:a.role==="user"?w.user:w.assistant,children:[a.text||(a.streaming?"\u2026":""),a.artifacts&&a.artifacts.length>0?H("div",{style:w.artifacts,children:a.artifacts.map(c=>H(ne,{artifact:c},c.filename))}):null]},a.id))}),r?H("div",{style:w.error,children:r}):null,U("form",{onSubmit:l,style:w.form,children:[H("input",{"aria-label":"Message",onChange:a=>s(a.target.value),placeholder:"Type a message\u2026",style:w.input,value:i}),H("button",{disabled:d||i.trim().length===0,style:w.button,type:"submit",children:"Send"})]})]})};var ie=/\/+$/,D=e=>{let t=e.sessionId,i=e.baseUrl.replace(ie,""),s=r=>`${i}/endpoint/${encodeURIComponent(e.token)}${r}`,n={"content-type":"application/json"},d=(r,o)=>({url:s(r),headers:n,body:JSON.stringify({message:o,...t?{sessionId:t}:{},...e.identity?{externalUserId:e.identity.externalUserId,userHmac:e.identity.userHmac}:{}})});return{sessionId:()=>t,getInfo:()=>b(s("/config"),{}),sendMessage:async r=>{let o=await _(d("/message",r));return t=o.sessionId,o},streamMessage:(r,o,l)=>F(d("/message/stream",r),o,a=>{t=a},l),subscribe:(r,o)=>{let l=o?.since??"0",a=!1,c,y=o?.intervalMs??3e3,u=async()=>{let g=t;if(g)try{let m=await b(s(`/messages?session=${encodeURIComponent(g)}&since=${encodeURIComponent(l)}`),{});for(let f of m.messages)r.onMessage(f);l=m.cursor}catch(m){r.onError?.(x(m))}a||(c=setTimeout(u,y))},p=()=>{a=!0,c&&clearTimeout(c)};return o?.signal?.addEventListener("abort",p),c=setTimeout(u,0),p},requestHuman:async()=>{t&&await fetch(s("/request-human"),{method:"POST",headers:n,body:JSON.stringify({sessionId:t})})}}};var le=e=>"transport"in e?e.transport:D(e);var ce=/\/+$/,T="/api/v1/end-user",v=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function de(e){let t="";for(let s=0;s<e.length;s+=32768)t+=String.fromCharCode(...e.subarray(s,s+32768));return btoa(t)}var pe=e=>{let t=e.baseUrl.replace(ce,""),i=()=>typeof e.token=="function"?e.token():e.token,s=async()=>({"content-type":"application/json",authorization:`Bearer ${await i()}`}),n=async(r,o)=>{try{let l=await fetch(`${t}${T}${r}`,{method:o.method,headers:await s(),...o.body===void 0?{}:{body:JSON.stringify(o.body)}});if(l.status===409){let a=await l.json().catch(()=>({}));throw new v(a.message??"Someone else saved this first.")}if(!l.ok){let a=await l.text().catch(()=>""),c=`${o.method} ${r} \u2192 ${String(l.status)}`;try{let y=JSON.parse(a);y.message&&(c=y.message)}catch{c=a?`${c}: ${a}`:c}throw x(new Error(c))}return await l.json()}catch(l){throw x(l)}},d=r=>`/tables/${encodeURIComponent(r)}`;return{listTables:async()=>(await b(`${t}${T}/tables`,await s())).tables,listRows:async(r,o)=>await b(`${t}${T}${d(r)}/rows?limit=${String(o??100)}`,await s()),insertRow:async(r,o)=>{let{rowIds:l}=await n(`${d(r)}/rows`,{method:"POST",body:{rows:[o]}}),a=l[0];if(!a)throw x(new Error("The row was not created."));return a},patchRow:async(r,o,l)=>(await n(`${d(r)}/rows/${encodeURIComponent(o)}`,{method:"PATCH",body:l})).updated,deleteRow:async(r,o)=>(await n(`/${encodeURIComponent(r)}/${encodeURIComponent(o)}`,{method:"DELETE"})).deleted,listFiles:async()=>(await b(`${t}${T}/tables/files/rows?limit=200`,await s())).rows.map(o=>({id:o.rowId,filename:String(o.data.filename??""),source:String(o.data.source??"client"),campaign:String(o.data.campaign??""),size:typeof o.data.size=="number"?o.data.size:0,createdAt:o.createdAt})),uploadFile:async(r,o)=>{let l=new Uint8Array(await r.arrayBuffer());return await n("/files",{method:"POST",body:{filename:r.name,mime:r.type||"application/octet-stream",data:de(l),...o===void 0?{}:{message:o}}})},fileLink:async r=>await b(`${t}${T}/files/${encodeURIComponent(r)}/url`,await s()),deleteFile:async r=>(await n(`/files/${encodeURIComponent(r)}`,{method:"DELETE"})).deleted,listDocuments:async()=>(await b(`${t}${T}/documents`,await s())).documents,readDocument:async r=>(await b(`${t}${T}/documents/${encodeURIComponent(r)}`,await s())).document,saveDocument:async(r,o)=>await n(`/documents/${encodeURIComponent(r)}`,{method:"PATCH",body:o})}};import{useCallback as ue,useEffect as j,useRef as me,useState as I}from"react";var N="greeting";function z(e,t){if(e.some(n=>n.id===t.id))return e;let i=e.filter(n=>n.id!==N);return[...t.author==="client"?i.filter(n=>!(n.pending&&n.author==="client"&&n.text===t.text)):i,t]}var ge=(e,t)=>{let[i,s]=I([]),[n,d]=I(""),[r,o]=I(!1),[l,a]=I(null),c=me(0),y=t?.pollMs??2e3;j(()=>{let p=!0;return e.getInfo().then(g=>{p&&g.greeting&&s(m=>m.length===0?[{id:N,cursor:"",author:"agent",text:g.greeting}]:m)}).catch(()=>{}),()=>{p=!1}},[e]),j(()=>e.subscribe?.({onMessage:p=>s(g=>z(g,p)),onError:p=>a(p.error)},{intervalMs:y}),[e,y]);let u=ue(()=>{let p=n.trim();if(!p||r)return;d(""),a(null),o(!0),c.current+=1;let g={id:`pending-${c.current}`,cursor:"",author:"client",text:p,pending:!0};s(m=>[...m,g]),e.sendMessage(p).then(()=>o(!1)).catch(m=>{a(m instanceof Error?m.message:"send_failed"),o(!1)})},[n,r,e]);return{messages:i,input:n,setInput:d,send:u,sending:r,error:l}};export{ae as Chat,Z as HolaProvider,v as PortalConflict,K as createApiTransport,le as createHolaClient,pe as createPortalData,D as createWidgetTransport,z as mergeMessage,E as textDeltaOf,S as thinkingDeltaOf,$ as toolEventOf,k as useArtifactUrl,A as useChat,M as useHolaClient,ge as useThread};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holaboss/client",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.4",
|
|
4
4
|
"description": "Injectable client SDK for embedding a Holaboss AI employee (chat today; dashboard + artifacts next) into your own web app.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"dev": "tsup --watch",
|
|
22
22
|
"build": "tsup",
|
|
23
23
|
"check-types": "tsc --noEmit",
|
|
24
|
-
"prepublishOnly": "bun run build"
|
|
24
|
+
"prepublishOnly": "bun run build",
|
|
25
|
+
"test": "vitest run"
|
|
25
26
|
},
|
|
26
27
|
"peerDependencies": {
|
|
27
28
|
"react": ">=18"
|
|
@@ -31,7 +32,8 @@
|
|
|
31
32
|
"@types/react-dom": "^19.1.9",
|
|
32
33
|
"tsconfig": "workspace:*",
|
|
33
34
|
"tsup": "^8.3.5",
|
|
34
|
-
"typescript": "catalog:"
|
|
35
|
+
"typescript": "catalog:",
|
|
36
|
+
"vitest": "^4.1.0"
|
|
35
37
|
},
|
|
36
38
|
"keywords": [
|
|
37
39
|
"ai",
|