@holaboss/client 0.2.0-beta.0 → 0.2.0-beta.2
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 +53 -3
- package/dist/index.d.ts +53 -3
- package/dist/index.js +5 -5
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
var F=Object.defineProperty;var
|
|
3
|
-
`))
|
|
4
|
-
`)}:null},
|
|
2
|
+
var F=Object.defineProperty;var Q=Object.getOwnPropertyDescriptor;var X=Object.getOwnPropertyNames;var Z=Object.prototype.hasOwnProperty;var ee=(e,t)=>{for(var n in t)F(e,n,{get:t[n],enumerable:!0})},te=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of X(t))!Z.call(e,a)&&a!==n&&F(e,a,{get:()=>t[a],enumerable:!(o=Q(t,a))||o.enumerable});return e};var re=e=>te(F({},"__esModule",{value:!0}),e);var pe={};ee(pe,{Chat:()=>G,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:()=>Y});module.exports=re(pe);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 oe=/^ /,C=e=>{if(e&&typeof e=="object"){let t=e,n=typeof t.error=="string"?t.error:"request_failed";return typeof t.retryable=="boolean"?{error:n,retryable:t.retryable}:{error:n}}return{error:"request_failed"}},se=e=>{let t="message",n=[];for(let o of e.split(`
|
|
3
|
+
`))o.startsWith(":")||(o.startsWith("event:")?t=o.slice(6).trim():o.startsWith("data:")&&n.push(o.slice(5).replace(oe,"")));return n.length>0?{event:t,data:n.join(`
|
|
4
|
+
`)}:null},ne=(e,t,n)=>{let o;try{o=JSON.parse(e.data)}catch{return}if(e.event==="session"){let a=o.sessionId;a&&(n(a),t.onSession?.(a));return}if(e.event==="turn_event"){t.onEvent?.(o);let a=I(o);a&&t.onText?.(a);let d=R(o);d&&t.onThinking?.(d);let r=E(o);r&&t.onTool?.(r);return}if(e.event==="chat_completed"){let a=o;n(a.sessionId),t.onComplete?.(a);return}e.event==="chat_error"&&t.onError?.(C(o))},h=async(e,t)=>{let n=await fetch(e,{headers:t});if(!n.ok)throw new Error(`hola: request failed (${n.status})`);return await n.json()},N=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()},j=async(e,t,n,o)=>{let a=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body,signal:o});if(!(a.ok&&a.body)){t.onError?.({error:`stream_failed_${a.status}`});return}let d=a.body.getReader(),r=new TextDecoder,s="",l=()=>{let c=s.indexOf(`
|
|
5
5
|
|
|
6
|
-
`);for(;
|
|
6
|
+
`);for(;c!==-1;){let f=s.slice(0,c);s=s.slice(c+2);let u=se(f);u&&ne(u,t,n),c=s.indexOf(`
|
|
7
7
|
|
|
8
|
-
`)}},i=async()=>{let
|
|
8
|
+
`)}},i=async()=>{let c=await d.read();if(!c.done)return s+=r.decode(c.value,{stream:!0}),l(),i()};await i()};var ae=/\/+$/,S="/api/v1/end-user",z=e=>{let t=e.baseUrl.replace(ae,""),n=()=>typeof e.token=="function"?e.token():e.token,o=async()=>({"content-type":"application/json",authorization:`Bearer ${await n()}`}),a=async d=>{let r=await fetch(`${t}${S}/chat`,{method:"POST",headers:await o(),body:JSON.stringify({message:d})});if(!r.ok)throw new Error(`hola: message failed (${r.status})`);return{reply:(await r.json()).reply??"",sessionId:"",artifacts:[]}};return{sessionId:()=>"",getInfo:async()=>h(`${t}${S}/chat/info`,await o()),sendMessage:a,streamMessage:async(d,r)=>{try{let s=await a(d);r.onText?.(s.reply),r.onComplete?.(s)}catch(s){r.onError?.(C(s))}},subscribe:(d,r)=>{let s=r?.since??"0",l=!1,i,c=r?.intervalMs??3e3,f=async()=>{try{let p=await h(`${t}${S}/chat/thread?since=${encodeURIComponent(s)}`,await o());for(let y of p.messages)d.onMessage(y);s=p.cursor}catch(p){d.onError?.(C(p))}l||(i=setTimeout(f,c))},u=()=>{l=!0,i&&clearTimeout(i)};return r?.signal?.addEventListener("abort",u),i=setTimeout(f,0),u},requestHuman:async()=>{await fetch(`${t}${S}/chat/request-human`,{method:"POST",headers:await o(),body:"{}"})}}};var $=require("react"),k=(e,t)=>{let[n,o]=(0,$.useState)("");return(0,$.useEffect)(()=>{if(!e){o("");return}let a=Uint8Array.from(atob(e),r=>r.charCodeAt(0)),d=URL.createObjectURL(new Blob([a],{type:t}));return o(d),()=>URL.revokeObjectURL(d)},[e,t]),n};var D=require("react");var M=require("react"),B=require("react/jsx-runtime"),J=(0,M.createContext)(null),q=({client:e,children:t})=>(0,B.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,n]=(0,w.useState)([]),[o,a]=(0,w.useState)(""),[d,r]=(0,w.useState)(!1),[s,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&&n([{id:c(),role:"assistant",text:p.greeting}])}).catch(()=>{}),()=>{u=!1}},[e,c]);let f=(0,w.useCallback)(()=>{let u=o.trim();if(!u||d)return;a(""),l(null),r(!0);let p=c(),y=c();n(g=>[...g,{id:p,role:"user",text:u},{id:y,role:"assistant",text:"",streaming:!0}]);let m=g=>{n(H=>H.map(_=>_.id===y?g(_):_))};e.streamMessage(u,{onText:g=>m(H=>({...H,text:H.text+g})),onComplete:g=>{m(H=>({...H,text:g.reply||H.text,artifacts:g.artifacts,streaming:!1})),r(!1)},onError:g=>{m(H=>({...H,streaming:!1})),l(g.error),r(!1)}}).catch(g=>{m(H=>({...H,streaming:!1})),l(g instanceof Error?g.message:"stream_failed"),r(!1)})},[o,d,e,c]);return{messages:t,input:o,setInput:a,send:f,sending:d,error:s}};var b=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"}},ie=({artifact:e})=>{let t=k(e.dataBase64??"",e.mimeType),n=e.url??t;return n?e.mimeType.startsWith("image/")?(0,b.jsx)("a",{href:n,rel:"noopener noreferrer",target:"_blank",children:(0,b.jsx)("img",{alt:e.filename,src:n,style:T.image})}):(0,b.jsx)("a",{download:e.filename,href:n,style:T.link,children:e.filename}):null},G=()=>{let e=A(),{messages:t,input:n,setInput:o,send:a,sending:d,error:r}=U(e),s=(0,D.useRef)(null);(0,D.useEffect)(()=>{let i=s.current;i&&(i.scrollTop=i.scrollHeight)},[t]);let l=i=>{i.preventDefault(),a()};return(0,b.jsxs)("div",{style:T.root,children:[(0,b.jsx)("div",{ref:s,style:T.log,children:t.map(i=>(0,b.jsxs)("div",{style:i.role==="user"?T.user:T.assistant,children:[i.text||(i.streaming?"\u2026":""),i.artifacts&&i.artifacts.length>0?(0,b.jsx)("div",{style:T.artifacts,children:i.artifacts.map(c=>(0,b.jsx)(ie,{artifact:c},c.filename))}):null]},i.id))}),r?(0,b.jsx)("div",{style:T.error,children:r}):null,(0,b.jsxs)("form",{onSubmit:l,style:T.form,children:[(0,b.jsx)("input",{"aria-label":"Message",onChange:i=>o(i.target.value),placeholder:"Type a message\u2026",style:T.input,value:n}),(0,b.jsx)("button",{disabled:d||n.trim().length===0,style:T.button,type:"submit",children:"Send"})]})]})};var le=/\/+$/,O=e=>{let t=e.sessionId,n=e.baseUrl.replace(le,""),o=r=>`${n}/endpoint/${encodeURIComponent(e.token)}${r}`,a={"content-type":"application/json"},d=(r,s)=>({url:o(r),headers:a,body:JSON.stringify({message:s,...t?{sessionId:t}:{},...e.identity?{externalUserId:e.identity.externalUserId,userHmac:e.identity.userHmac}:{}})});return{sessionId:()=>t,getInfo:()=>h(o("/config"),{}),sendMessage:async r=>{let s=await N(d("/message",r));return t=s.sessionId,s},streamMessage:(r,s,l)=>j(d("/message/stream",r),s,i=>{t=i},l),subscribe:(r,s)=>{let l=s?.since??"0",i=!1,c,f=s?.intervalMs??3e3,u=async()=>{let y=t;if(y)try{let m=await h(o(`/messages?session=${encodeURIComponent(y)}&since=${encodeURIComponent(l)}`),{});for(let g of m.messages)r.onMessage(g);l=m.cursor}catch(m){r.onError?.(C(m))}i||(c=setTimeout(u,f))},p=()=>{i=!0,c&&clearTimeout(c)};return s?.signal?.addEventListener("abort",p),c=setTimeout(u,0),p},requestHuman:async()=>{t&&await fetch(o("/request-human"),{method:"POST",headers:a,body:JSON.stringify({sessionId:t})})}}};var K=e=>"transport"in e?e.transport:O(e);var ce=/\/+$/,P="/api/v1/end-user",v=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function de(e){let t="";for(let o=0;o<e.length;o+=32768)t+=String.fromCharCode(...e.subarray(o,o+32768));return btoa(t)}var V=e=>{let t=e.baseUrl.replace(ce,""),n=()=>typeof e.token=="function"?e.token():e.token,o=async()=>({"content-type":"application/json",authorization:`Bearer ${await n()}`}),a=async(r,s)=>{try{let l=await fetch(`${t}${P}${r}`,{method:s.method,headers:await o(),...s.body===void 0?{}:{body:JSON.stringify(s.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=`${s.method} ${r} \u2192 ${String(l.status)}`;try{let f=JSON.parse(i);f.message&&(c=f.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 o())).tables,listRows:async(r,s)=>await h(`${t}${P}${d(r)}/rows?limit=${String(s??100)}`,await o()),insertRow:async(r,s)=>{let{rowIds:l}=await a(`${d(r)}/rows`,{method:"POST",body:{rows:[s]}}),i=l[0];if(!i)throw C(new Error("The row was not created."));return i},patchRow:async(r,s,l)=>(await a(`${d(r)}/rows/${encodeURIComponent(s)}`,{method:"PATCH",body:l})).updated,deleteRow:async(r,s)=>(await a(`/${encodeURIComponent(r)}/${encodeURIComponent(s)}`,{method:"DELETE"})).deleted,listFiles:async()=>(await h(`${t}${P}/tables/files/rows?limit=200`,await o())).rows.map(s=>({id:s.rowId,filename:String(s.data.filename??""),source:String(s.data.source??"client"),campaign:String(s.data.campaign??""),size:typeof s.data.size=="number"?s.data.size:0,createdAt:s.createdAt})),uploadFile:async(r,s)=>{let l=new Uint8Array(await r.arrayBuffer());return await a("/files",{method:"POST",body:{filename:r.name,mime:r.type||"application/octet-stream",data:de(l),...s===void 0?{}:{message:s}}})},fileLink:async r=>await h(`${t}${P}/files/${encodeURIComponent(r)}/url`,await o()),deleteFile:async r=>(await a(`/files/${encodeURIComponent(r)}`,{method:"DELETE"})).deleted,listDocuments:async()=>(await h(`${t}${P}/documents`,await o())).documents,readDocument:async r=>(await h(`${t}${P}/documents/${encodeURIComponent(r)}`,await o())).document,saveDocument:async(r,s)=>await a(`/documents/${encodeURIComponent(r)}`,{method:"PATCH",body:s})}};var x=require("react");function L(e,t){return e.some(o=>o.id===t.id)?e:[...t.author==="client"?e.filter(o=>!(o.pending&&o.author==="client"&&o.text===t.text)):e,t]}var Y=(e,t)=>{let[n,o]=(0,x.useState)([]),[a,d]=(0,x.useState)(""),[r,s]=(0,x.useState)(!1),[l,i]=(0,x.useState)(null),c=(0,x.useRef)(0),f=t?.pollMs??2e3;(0,x.useEffect)(()=>{let p=!0;return e.getInfo().then(y=>{p&&y.greeting&&o(m=>m.length===0?[{id:"greeting",cursor:"",author:"agent",text:y.greeting}]:m)}).catch(()=>{}),()=>{p=!1}},[e]),(0,x.useEffect)(()=>e.subscribe?.({onMessage:p=>o(y=>L(y,p)),onError:p=>i(p.error)},{intervalMs:f}),[e,f]);let u=(0,x.useCallback)(()=>{let p=a.trim();if(!p||r)return;d(""),i(null),s(!0),c.current+=1;let y={id:`pending-${c.current}`,cursor:"",author:"client",text:p,pending:!0};o(m=>[...m,y]),e.sendMessage(p).then(()=>s(!1)).catch(m=>{i(m instanceof Error?m.message:"send_failed"),s(!1)})},[a,r,e]);return{messages:n,input:a,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
|
@@ -197,6 +197,29 @@ type PortalFile = {
|
|
|
197
197
|
size: number;
|
|
198
198
|
createdAt: string;
|
|
199
199
|
};
|
|
200
|
+
/** A document shared with this customer. The body arrives only on a read. */
|
|
201
|
+
type PortalDocumentSummary = {
|
|
202
|
+
documentId: string;
|
|
203
|
+
title: string;
|
|
204
|
+
version: number;
|
|
205
|
+
/** Whether the company opened this one for editing, not merely for reading. */
|
|
206
|
+
editable: boolean;
|
|
207
|
+
updatedAt: string;
|
|
208
|
+
};
|
|
209
|
+
type PortalDocument = PortalDocumentSummary & {
|
|
210
|
+
body: string;
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* A save that lost a race.
|
|
214
|
+
*
|
|
215
|
+
* Thrown rather than returned so it cannot be mistaken for success, and separate
|
|
216
|
+
* from a plain error because the caller should do something different: re-read,
|
|
217
|
+
* reapply, save again — never retry the same bytes, which would overwrite whoever
|
|
218
|
+
* got there first.
|
|
219
|
+
*/
|
|
220
|
+
declare class PortalConflict extends Error {
|
|
221
|
+
constructor(message: string);
|
|
222
|
+
}
|
|
200
223
|
type PortalData = {
|
|
201
224
|
/** The tables this customer can see. A table the company has not shared is
|
|
202
225
|
* absent, not empty — it answers as if it did not exist. */
|
|
@@ -222,10 +245,37 @@ type PortalData = {
|
|
|
222
245
|
uploadFile(file: File, message?: string): Promise<{
|
|
223
246
|
rowId: string;
|
|
224
247
|
}>;
|
|
225
|
-
/**
|
|
226
|
-
|
|
248
|
+
/**
|
|
249
|
+
* A link the browser can actually follow.
|
|
250
|
+
*
|
|
251
|
+
* Not a plain URL string: the download route needs the customer's token, and
|
|
252
|
+
* navigation from an `<a href>` carries no Authorization header — so a direct
|
|
253
|
+
* link is a guaranteed 401. This fetches the signed URL with the token, and
|
|
254
|
+
* what comes back needs no header of its own.
|
|
255
|
+
*/
|
|
256
|
+
fileLink(rowId: string): Promise<{
|
|
257
|
+
url: string;
|
|
258
|
+
filename: string;
|
|
259
|
+
}>;
|
|
227
260
|
/** Only files this customer sent, and only before they are filed. */
|
|
228
261
|
deleteFile(rowId: string): Promise<number>;
|
|
262
|
+
/** Documents the company shared with this customer. */
|
|
263
|
+
listDocuments(): Promise<PortalDocumentSummary[]>;
|
|
264
|
+
readDocument(documentId: string): Promise<PortalDocument>;
|
|
265
|
+
/**
|
|
266
|
+
* Save new text.
|
|
267
|
+
*
|
|
268
|
+
* `baseVersion` is the version that was open. Rejects with a PortalConflict if
|
|
269
|
+
* it has moved — do not retry with the same text: read again, put the change
|
|
270
|
+
* back on top of theirs, and save that.
|
|
271
|
+
*/
|
|
272
|
+
saveDocument(documentId: string, edit: {
|
|
273
|
+
body: string;
|
|
274
|
+
baseVersion: number;
|
|
275
|
+
note?: string;
|
|
276
|
+
}): Promise<{
|
|
277
|
+
version: number;
|
|
278
|
+
}>;
|
|
229
279
|
};
|
|
230
280
|
declare const createPortalData: (config: ApiTransportConfig) => PortalData;
|
|
231
281
|
|
|
@@ -281,4 +331,4 @@ declare const useThread: (client: HolaClient, options?: {
|
|
|
281
331
|
pollMs?: number;
|
|
282
332
|
}) => UseThread;
|
|
283
333
|
|
|
284
|
-
export { type ApiTransportConfig, Chat, type ChatMessage, type CreateHolaClientOptions, type HolaArtifact, type HolaAuthor, type HolaClient, type HolaCompletion, type HolaEmployeeInfo, type HolaError, type HolaIdentity, type HolaMessage, HolaProvider, type HolaStreamHandlers, type HolaSubscribeHandlers, type HolaSubscribeOptions, type HolaToolEvent, type HolaTransport, type PortalData, type PortalField, type PortalFile, type PortalRow, type PortalRowPage, type PortalTable, type ThreadMessage, type UseChat, type UseThread, type WidgetTransportConfig, createApiTransport, createHolaClient, createPortalData, createWidgetTransport, mergeMessage, textDeltaOf, thinkingDeltaOf, toolEventOf, useArtifactUrl, useChat, useHolaClient, useThread };
|
|
334
|
+
export { type ApiTransportConfig, Chat, type ChatMessage, type CreateHolaClientOptions, type HolaArtifact, type HolaAuthor, type HolaClient, type HolaCompletion, type HolaEmployeeInfo, type HolaError, type HolaIdentity, type HolaMessage, HolaProvider, type HolaStreamHandlers, type HolaSubscribeHandlers, type HolaSubscribeOptions, type HolaToolEvent, type HolaTransport, PortalConflict, type PortalData, type PortalDocument, type PortalDocumentSummary, type PortalField, type PortalFile, type PortalRow, type PortalRowPage, type PortalTable, type ThreadMessage, type UseChat, type UseThread, type WidgetTransportConfig, createApiTransport, createHolaClient, createPortalData, createWidgetTransport, mergeMessage, textDeltaOf, thinkingDeltaOf, toolEventOf, useArtifactUrl, useChat, useHolaClient, useThread };
|
package/dist/index.d.ts
CHANGED
|
@@ -197,6 +197,29 @@ type PortalFile = {
|
|
|
197
197
|
size: number;
|
|
198
198
|
createdAt: string;
|
|
199
199
|
};
|
|
200
|
+
/** A document shared with this customer. The body arrives only on a read. */
|
|
201
|
+
type PortalDocumentSummary = {
|
|
202
|
+
documentId: string;
|
|
203
|
+
title: string;
|
|
204
|
+
version: number;
|
|
205
|
+
/** Whether the company opened this one for editing, not merely for reading. */
|
|
206
|
+
editable: boolean;
|
|
207
|
+
updatedAt: string;
|
|
208
|
+
};
|
|
209
|
+
type PortalDocument = PortalDocumentSummary & {
|
|
210
|
+
body: string;
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* A save that lost a race.
|
|
214
|
+
*
|
|
215
|
+
* Thrown rather than returned so it cannot be mistaken for success, and separate
|
|
216
|
+
* from a plain error because the caller should do something different: re-read,
|
|
217
|
+
* reapply, save again — never retry the same bytes, which would overwrite whoever
|
|
218
|
+
* got there first.
|
|
219
|
+
*/
|
|
220
|
+
declare class PortalConflict extends Error {
|
|
221
|
+
constructor(message: string);
|
|
222
|
+
}
|
|
200
223
|
type PortalData = {
|
|
201
224
|
/** The tables this customer can see. A table the company has not shared is
|
|
202
225
|
* absent, not empty — it answers as if it did not exist. */
|
|
@@ -222,10 +245,37 @@ type PortalData = {
|
|
|
222
245
|
uploadFile(file: File, message?: string): Promise<{
|
|
223
246
|
rowId: string;
|
|
224
247
|
}>;
|
|
225
|
-
/**
|
|
226
|
-
|
|
248
|
+
/**
|
|
249
|
+
* A link the browser can actually follow.
|
|
250
|
+
*
|
|
251
|
+
* Not a plain URL string: the download route needs the customer's token, and
|
|
252
|
+
* navigation from an `<a href>` carries no Authorization header — so a direct
|
|
253
|
+
* link is a guaranteed 401. This fetches the signed URL with the token, and
|
|
254
|
+
* what comes back needs no header of its own.
|
|
255
|
+
*/
|
|
256
|
+
fileLink(rowId: string): Promise<{
|
|
257
|
+
url: string;
|
|
258
|
+
filename: string;
|
|
259
|
+
}>;
|
|
227
260
|
/** Only files this customer sent, and only before they are filed. */
|
|
228
261
|
deleteFile(rowId: string): Promise<number>;
|
|
262
|
+
/** Documents the company shared with this customer. */
|
|
263
|
+
listDocuments(): Promise<PortalDocumentSummary[]>;
|
|
264
|
+
readDocument(documentId: string): Promise<PortalDocument>;
|
|
265
|
+
/**
|
|
266
|
+
* Save new text.
|
|
267
|
+
*
|
|
268
|
+
* `baseVersion` is the version that was open. Rejects with a PortalConflict if
|
|
269
|
+
* it has moved — do not retry with the same text: read again, put the change
|
|
270
|
+
* back on top of theirs, and save that.
|
|
271
|
+
*/
|
|
272
|
+
saveDocument(documentId: string, edit: {
|
|
273
|
+
body: string;
|
|
274
|
+
baseVersion: number;
|
|
275
|
+
note?: string;
|
|
276
|
+
}): Promise<{
|
|
277
|
+
version: number;
|
|
278
|
+
}>;
|
|
229
279
|
};
|
|
230
280
|
declare const createPortalData: (config: ApiTransportConfig) => PortalData;
|
|
231
281
|
|
|
@@ -281,4 +331,4 @@ declare const useThread: (client: HolaClient, options?: {
|
|
|
281
331
|
pollMs?: number;
|
|
282
332
|
}) => UseThread;
|
|
283
333
|
|
|
284
|
-
export { type ApiTransportConfig, Chat, type ChatMessage, type CreateHolaClientOptions, type HolaArtifact, type HolaAuthor, type HolaClient, type HolaCompletion, type HolaEmployeeInfo, type HolaError, type HolaIdentity, type HolaMessage, HolaProvider, type HolaStreamHandlers, type HolaSubscribeHandlers, type HolaSubscribeOptions, type HolaToolEvent, type HolaTransport, type PortalData, type PortalField, type PortalFile, type PortalRow, type PortalRowPage, type PortalTable, type ThreadMessage, type UseChat, type UseThread, type WidgetTransportConfig, createApiTransport, createHolaClient, createPortalData, createWidgetTransport, mergeMessage, textDeltaOf, thinkingDeltaOf, toolEventOf, useArtifactUrl, useChat, useHolaClient, useThread };
|
|
334
|
+
export { type ApiTransportConfig, Chat, type ChatMessage, type CreateHolaClientOptions, type HolaArtifact, type HolaAuthor, type HolaClient, type HolaCompletion, type HolaEmployeeInfo, type HolaError, type HolaIdentity, type HolaMessage, HolaProvider, type HolaStreamHandlers, type HolaSubscribeHandlers, type HolaSubscribeOptions, type HolaToolEvent, type HolaTransport, PortalConflict, type PortalData, type PortalDocument, type PortalDocumentSummary, type PortalField, type PortalFile, type PortalRow, type PortalRowPage, type PortalTable, type ThreadMessage, type UseChat, type UseThread, type WidgetTransportConfig, createApiTransport, createHolaClient, createPortalData, createWidgetTransport, mergeMessage, textDeltaOf, thinkingDeltaOf, toolEventOf, useArtifactUrl, useChat, useHolaClient, useThread };
|
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},
|
|
3
|
-
`))s.startsWith(":")||(s.startsWith("event:")?t=s.slice(6).trim():s.startsWith("data:")&&a.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 z=/^ /,x=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"}},J=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(z,"")));return a.length>0?{event:t,data:a.join(`
|
|
4
|
+
`)}:null},q=(e,t,a)=>{let s;try{s=JSON.parse(e.data)}catch{return}if(e.event==="session"){let i=s.sessionId;i&&(a(i),t.onSession?.(i));return}if(e.event==="turn_event"){t.onEvent?.(s);let i=E(s);i&&t.onText?.(i);let d=S(s);d&&t.onThinking?.(d);let r=$(s);r&&t.onTool?.(r);return}if(e.event==="chat_completed"){let i=s;a(i.sessionId),t.onComplete?.(i);return}e.event==="chat_error"&&t.onError?.(x(s))},b=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()},_=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,a,s)=>{let i=await fetch(e.url,{method:"POST",headers:e.headers,body:e.body,signal:s});if(!(i.ok&&i.body)){t.onError?.({error:`stream_failed_${i.status}`});return}let d=i.body.getReader(),r=new TextDecoder,o="",l=()=>{let c=o.indexOf(`
|
|
5
5
|
|
|
6
|
-
`);for(;
|
|
6
|
+
`);for(;c!==-1;){let f=o.slice(0,c);o=o.slice(c+2);let u=J(f);u&&q(u,t,a),c=o.indexOf(`
|
|
7
7
|
|
|
8
|
-
`)}},n=async()=>{let
|
|
8
|
+
`)}},n=async()=>{let c=await d.read();if(!c.done)return o+=r.decode(c.value,{stream:!0}),l(),n()};await n()};var B=/\/+$/,C="/api/v1/end-user",G=e=>{let t=e.baseUrl.replace(B,""),a=()=>typeof e.token=="function"?e.token():e.token,s=async()=>({"content-type":"application/json",authorization:`Bearer ${await a()}`}),i=async d=>{let r=await fetch(`${t}${C}/chat`,{method:"POST",headers:await s(),body:JSON.stringify({message:d})});if(!r.ok)throw new Error(`hola: message failed (${r.status})`);return{reply:(await r.json()).reply??"",sessionId:"",artifacts:[]}};return{sessionId:()=>"",getInfo:async()=>b(`${t}${C}/chat/info`,await s()),sendMessage:i,streamMessage:async(d,r)=>{try{let o=await i(d);r.onText?.(o.reply),r.onComplete?.(o)}catch(o){r.onError?.(x(o))}},subscribe:(d,r)=>{let o=r?.since??"0",l=!1,n,c=r?.intervalMs??3e3,f=async()=>{try{let p=await b(`${t}${C}/chat/thread?since=${encodeURIComponent(o)}`,await s());for(let y of p.messages)d.onMessage(y);o=p.cursor}catch(p){d.onError?.(x(p))}l||(n=setTimeout(f,c))},u=()=>{l=!0,n&&clearTimeout(n)};return r?.signal?.addEventListener("abort",u),n=setTimeout(f,0),u},requestHuman:async()=>{await fetch(`${t}${C}/chat/request-human`,{method:"POST",headers:await s(),body:"{}"})}}};import{useEffect as K,useState as V}from"react";var k=(e,t)=>{let[a,s]=V("");return K(()=>{if(!e){s("");return}let i=Uint8Array.from(atob(e),r=>r.charCodeAt(0)),d=URL.createObjectURL(new Blob([i],{type:t}));return s(d),()=>URL.revokeObjectURL(d)},[e,t]),a};import{useEffect as re,useRef as oe}from"react";import{createContext as Y,useContext as Q}from"react";import{jsx as Z}from"react/jsx-runtime";var L=Y(null),X=({client:e,children:t})=>Z(L.Provider,{value:e,children:t}),M=()=>{let e=Q(L);if(!e)throw new Error("useHolaClient must be used within <HolaProvider>");return e};import{useCallback as W,useEffect as ee,useRef as te,useState as P}from"react";var A=e=>{let[t,a]=P([]),[s,i]=P(""),[d,r]=P(!1),[o,l]=P(null),n=te(0),c=W(()=>{let u=n.current;return n.current+=1,u},[]);ee(()=>{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 f=W(()=>{let u=s.trim();if(!u||d)return;i(""),l(null),r(!0);let p=c(),y=c();a(g=>[...g,{id:p,role:"user",text:u},{id:y,role:"assistant",text:"",streaming:!0}]);let m=g=>{a(h=>h.map(R=>R.id===y?g(R):R))};e.streamMessage(u,{onText:g=>m(h=>({...h,text:h.text+g})),onComplete:g=>{m(h=>({...h,text:g.reply||h.text,artifacts:g.artifacts,streaming:!1})),r(!1)},onError:g=>{m(h=>({...h,streaming:!1})),l(g.error),r(!1)}}).catch(g=>{m(h=>({...h,streaming:!1})),l(g instanceof Error?g.message:"stream_failed"),r(!1)})},[s,d,e,c]);return{messages:t,input:s,setInput:i,send:f,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"}},se=({artifact:e})=>{let t=k(e.dataBase64??"",e.mimeType),a=e.url??t;return a?e.mimeType.startsWith("image/")?H("a",{href:a,rel:"noopener noreferrer",target:"_blank",children:H("img",{alt:e.filename,src:a,style:w.image})}):H("a",{download:e.filename,href:a,style:w.link,children:e.filename}):null},ne=()=>{let e=M(),{messages:t,input:a,setInput:s,send:i,sending:d,error:r}=A(e),o=oe(null);re(()=>{let n=o.current;n&&(n.scrollTop=n.scrollHeight)},[t]);let l=n=>{n.preventDefault(),i()};return U("div",{style:w.root,children:[H("div",{ref:o,style:w.log,children:t.map(n=>U("div",{style:n.role==="user"?w.user:w.assistant,children:[n.text||(n.streaming?"\u2026":""),n.artifacts&&n.artifacts.length>0?H("div",{style:w.artifacts,children:n.artifacts.map(c=>H(se,{artifact:c},c.filename))}):null]},n.id))}),r?H("div",{style:w.error,children:r}):null,U("form",{onSubmit:l,style:w.form,children:[H("input",{"aria-label":"Message",onChange:n=>s(n.target.value),placeholder:"Type a message\u2026",style:w.input,value:a}),H("button",{disabled:d||a.trim().length===0,style:w.button,type:"submit",children:"Send"})]})]})};var ae=/\/+$/,D=e=>{let t=e.sessionId,a=e.baseUrl.replace(ae,""),s=r=>`${a}/endpoint/${encodeURIComponent(e.token)}${r}`,i={"content-type":"application/json"},d=(r,o)=>({url:s(r),headers:i,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,n=>{t=n},l),subscribe:(r,o)=>{let l=o?.since??"0",n=!1,c,f=o?.intervalMs??3e3,u=async()=>{let y=t;if(y)try{let m=await b(s(`/messages?session=${encodeURIComponent(y)}&since=${encodeURIComponent(l)}`),{});for(let g of m.messages)r.onMessage(g);l=m.cursor}catch(m){r.onError?.(x(m))}n||(c=setTimeout(u,f))},p=()=>{n=!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:i,body:JSON.stringify({sessionId:t})})}}};var ie=e=>"transport"in e?e.transport:D(e);var le=/\/+$/,T="/api/v1/end-user",v=class extends Error{constructor(t){super(t),this.name="PortalConflict"}};function ce(e){let t="";for(let s=0;s<e.length;s+=32768)t+=String.fromCharCode(...e.subarray(s,s+32768));return btoa(t)}var de=e=>{let t=e.baseUrl.replace(le,""),a=()=>typeof e.token=="function"?e.token():e.token,s=async()=>({"content-type":"application/json",authorization:`Bearer ${await a()}`}),i=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 n=await l.json().catch(()=>({}));throw new v(n.message??"Someone else saved this first.")}if(!l.ok){let n=await l.text().catch(()=>""),c=`${o.method} ${r} \u2192 ${String(l.status)}`;try{let f=JSON.parse(n);f.message&&(c=f.message)}catch{c=n?`${c}: ${n}`: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 i(`${d(r)}/rows`,{method:"POST",body:{rows:[o]}}),n=l[0];if(!n)throw x(new Error("The row was not created."));return n},patchRow:async(r,o,l)=>(await i(`${d(r)}/rows/${encodeURIComponent(o)}`,{method:"PATCH",body:l})).updated,deleteRow:async(r,o)=>(await i(`/${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 i("/files",{method:"POST",body:{filename:r.name,mime:r.type||"application/octet-stream",data:ce(l),...o===void 0?{}:{message:o}}})},fileLink:async r=>await b(`${t}${T}/files/${encodeURIComponent(r)}/url`,await s()),deleteFile:async r=>(await i(`/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 i(`/documents/${encodeURIComponent(r)}`,{method:"PATCH",body:o})}};import{useCallback as pe,useEffect as N,useRef as ue,useState as I}from"react";function j(e,t){return e.some(s=>s.id===t.id)?e:[...t.author==="client"?e.filter(s=>!(s.pending&&s.author==="client"&&s.text===t.text)):e,t]}var me=(e,t)=>{let[a,s]=I([]),[i,d]=I(""),[r,o]=I(!1),[l,n]=I(null),c=ue(0),f=t?.pollMs??2e3;N(()=>{let p=!0;return e.getInfo().then(y=>{p&&y.greeting&&s(m=>m.length===0?[{id:"greeting",cursor:"",author:"agent",text:y.greeting}]:m)}).catch(()=>{}),()=>{p=!1}},[e]),N(()=>e.subscribe?.({onMessage:p=>s(y=>j(y,p)),onError:p=>n(p.error)},{intervalMs:f}),[e,f]);let u=pe(()=>{let p=i.trim();if(!p||r)return;d(""),n(null),o(!0),c.current+=1;let y={id:`pending-${c.current}`,cursor:"",author:"client",text:p,pending:!0};s(m=>[...m,y]),e.sendMessage(p).then(()=>o(!1)).catch(m=>{n(m instanceof Error?m.message:"send_failed"),o(!1)})},[i,r,e]);return{messages:a,input:i,setInput:d,send:u,sending:r,error:l}};export{ne as Chat,X as HolaProvider,v as PortalConflict,G as createApiTransport,ie as createHolaClient,de as createPortalData,D as createWidgetTransport,j as mergeMessage,E as textDeltaOf,S as thinkingDeltaOf,$ as toolEventOf,k as useArtifactUrl,A as useChat,M as useHolaClient,me 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.2",
|
|
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",
|