@usecrow/client 0.1.43 → 0.1.45

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 CHANGED
@@ -1,3 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const _=require("./workflowExecutor-D1pVvfZw.cjs");class k{constructor(){this.state={token:null,metadata:{},isVerified:!1},this.listeners=new Set}identify(e){const{token:t,...s}=e;this.state={token:t,metadata:s,isVerified:!1},this.notify(),console.log("[Crow] User identified")}setVerified(e){this.state={...this.state,isVerified:e},this.notify()}reset(){this.state={token:null,metadata:{},isVerified:!1},this.notify(),console.log("[Crow] User reset")}getToken(){return this.state.token}getState(){return{...this.state}}isIdentified(){return this.state.token!==null}isVerified(){return this.state.isVerified}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notify(){const e=this.getState();for(const t of this.listeners)t(e)}}class v{constructor(){this.handlers={}}register(e){for(const[t,s]of Object.entries(e))typeof s=="function"?(this.handlers[t]=s,console.log(`[Crow] Registered client tool: ${t}`)):console.warn(`[Crow] Skipping ${t}: handler is not a function`)}unregister(e){delete this.handlers[e],console.log(`[Crow] Unregistered client tool: ${e}`)}has(e){return e in this.handlers}getRegisteredTools(){return Object.keys(this.handlers)}async execute(e,t){const s=this.handlers[e];if(!s)return console.warn(`[Crow] No handler registered for tool: ${e}`),{status:"error",error:`No handler registered for tool: ${e}`};try{console.log(`[Crow] Executing client tool: ${e}`,t);const r=await s(t);return console.log(`[Crow] Tool ${e} completed:`,r),r}catch(r){const n=r instanceof Error?r.message:String(r);return console.error(`[Crow] Tool ${e} failed:`,r),{status:"error",error:n}}}}const E="crow_conv_";class C{constructor(e,t){this.currentId=null,this.productId=e,this.apiUrl=t,this.currentId=this.loadFromStorage()}getStorageKey(){return`${E}${this.productId}`}loadFromStorage(){try{return localStorage.getItem(this.getStorageKey())}catch{return null}}saveToStorage(e){try{localStorage.setItem(this.getStorageKey(),e)}catch{}}clearStorage(){try{localStorage.removeItem(this.getStorageKey())}catch{}}getCurrentId(){return this.currentId}setCurrentId(e){this.currentId=e,e?this.saveToStorage(e):this.clearStorage()}hasRestoredConversation(){return this.currentId!==null}clear(){this.currentId=null,this.clearStorage()}async getConversations(e){try{const t=await fetch(`${this.apiUrl}/api/chat/conversations?product_id=${this.productId}&identity_token=${encodeURIComponent(e)}`);if(!t.ok)throw new Error(`HTTP error: ${t.status}`);return(await t.json()).conversations||[]}catch(t){return console.error("[Crow] Failed to load conversations:",t),[]}}async loadHistory(e,t){try{const s=await fetch(`${this.apiUrl}/api/chat/conversations/${e}/history?product_id=${this.productId}&identity_token=${encodeURIComponent(t)}`);if(!s.ok)throw new Error(`HTTP error: ${s.status}`);const r=await s.json();return this.parseHistoryMessages(r.messages||[])}catch(s){return console.error("[Crow] Failed to load conversation history:",s),[]}}async loadAnonymousHistory(e){try{const t=await fetch(`${this.apiUrl}/api/chat/conversations/${e}/history/anonymous?product_id=${this.productId}`);if(!t.ok)throw new Error(`HTTP error: ${t.status}`);const s=await t.json();return this.parseHistoryMessages(s.messages||[])}catch(t){return console.error("[Crow] Failed to load anonymous conversation history:",t),[]}}parseHistoryMessages(e){return e.filter(t=>t.role!=="tool"&&!t.content.startsWith("[Client Tool Result:")).map((t,s)=>({id:`history-${s}`,content:this.parseContent(t.content),role:t.role==="assistant"?"assistant":"user",timestamp:new Date}))}parseContent(e){try{const t=JSON.parse(e);if(Array.isArray(t)){const s=t.find(r=>r.type==="text");return(s==null?void 0:s.text)||e}}catch{}if(e.includes("'type': 'text'")){const t=e.match(/\{'text':\s*'((?:[^'\\]|\\.)*)'\s*,\s*'type':\s*'text'/);if(t)return t[1].replace(/\\n/g,`
2
- `).replace(/\\'/g,"'")}return e}}function b(o){if(o==="[DONE]")return{type:"done"};try{const e=JSON.parse(o);switch(e.type){case"verification_status":return{type:"verification_status",isVerified:e.is_verified===!0};case"conversation_id":return{type:"conversation_id",conversationId:e.conversation_id};case"thinking":return e.status==="complete"?{type:"thinking_complete"}:null;case"thinking_token":return{type:"thinking",content:e.content||""};case"content":return{type:"content",text:e.content||"",accumulated:""};case"citations":return{type:"citations",citations:e.citations};case"error":return{type:"error",message:e.message||"Unknown error"};case"tool_call_start":return{type:"tool_call_start",toolName:e.tool_name,displayName:e.display_name||void 0,arguments:e.arguments||{}};case"tool_call_complete":return{type:"tool_call_complete",toolName:e.tool_name,displayName:e.display_name||void 0,success:e.success};case"client_tool_call":return{type:"client_tool_call",toolName:e.tool_name,displayName:e.display_name||void 0,arguments:e.arguments||{}};case"tool_consent_required":return{type:"tool_consent_required",toolName:e.tool_name,displayName:e.display_name||void 0,arguments:e.arguments||{}};case"workflow_started":return{type:"workflow_started",name:e.name,todos:e.todos};case"todo_updated":return{type:"workflow_todo_updated",todoId:e.id,status:e.status};case"workflow_ended":return{type:"workflow_ended"};case"workflow_complete_prompt":return{type:"workflow_complete_prompt"};default:return null}}catch{return console.error("[Crow] Failed to parse SSE data:",o),null}}function*M(o){const e=o.split(`
3
- `);for(const t of e)t.startsWith("data: ")&&(yield t.slice(6).trim())}async function*S(o,e){var n;const t=(n=o.body)==null?void 0:n.getReader();if(!t)throw new Error("Response body is not readable");const s=new TextDecoder;let r="";try{for(;;){if(e!=null&&e.aborted){t.cancel();return}const{done:i,value:d}=await t.read();if(i)break;const u=s.decode(d);for(const h of M(u)){const l=b(h);if(l&&(l.type==="content"?(r+=l.text,yield{...l,accumulated:r}):yield l,l.type==="done"))return}}}finally{t.releaseLock()}}async function L(){var o;try{const e=document.title,t=window.location.href,s=window.location.pathname,r=(((o=document.body)==null?void 0:o.innerText)||"").slice(0,2e3).trim();return{status:"success",data:{title:e,url:t,pathname:s,visibleText:r}}}catch(e){return{status:"error",error:e instanceof Error?e.message:"Failed to read screen"}}}const g={whatsOnScreen:L},T=Object.keys(g),$="https://api.usecrow.org",U="claude-sonnet-4-20250514";class x{constructor(e){this.context={},this.abortController=null,this.callbacks={},this._messages=[],this.messageListeners=new Set,this._isLoading=!1,this.loadingListeners=new Set,this.config={productId:e.productId,apiUrl:e.apiUrl||$,model:e.model||U,subdomain:e.subdomain},this.identity=new k,this.tools=new v,this.conversations=new C(this.config.productId,this.config.apiUrl),this.tools.register(g),console.log("[Crow] Default tools registered:",T.join(", ")),this.identity.subscribe(t=>{var s,r;(r=(s=this.callbacks).onVerificationStatus)==null||r.call(s,t.isVerified)})}get productId(){return this.config.productId}get apiUrl(){return this.config.apiUrl}get model(){return this.config.model}set model(e){this.config.model=e}on(e){this.callbacks={...this.callbacks,...e}}identify(e){this.identity.identify(e)}resetUser(){this.identity.reset(),this.clearMessages()}isIdentified(){return this.identity.isIdentified()}isVerified(){return this.identity.isVerified()}registerTools(e){this.tools.register(e)}unregisterTool(e){this.tools.unregister(e)}getRegisteredTools(){return this.tools.getRegisteredTools()}setContext(e){this.context={...this.context,...e}}clearContext(){this.context={}}get messages(){return[...this._messages]}get isLoading(){return this._isLoading}onMessages(e){return this.messageListeners.add(e),()=>this.messageListeners.delete(e)}onLoading(e){return this.loadingListeners.add(e),()=>this.loadingListeners.delete(e)}clearMessages(){this._messages=[],this.conversations.clear(),this.notifyMessages()}loadMessages(e){this._messages=e,this.notifyMessages()}notifyMessages(){const e=this.messages;for(const t of this.messageListeners)t(e)}setLoading(e){this._isLoading=e;for(const t of this.loadingListeners)t(e)}addMessage(e){var t,s;this._messages=[...this._messages,e],this.notifyMessages(),(s=(t=this.callbacks).onMessage)==null||s.call(t,e)}updateMessage(e,t){var s,r;this._messages=this._messages.map(n=>n.id===e?{...n,...t}:n),this.notifyMessages(),(r=(s=this.callbacks).onMessageUpdate)==null||r.call(s,e,t)}generateMessageId(e){return`${e}-${Date.now()}-${Math.random().toString(36).slice(2,9)}`}get conversationId(){return this.conversations.getCurrentId()}set conversationId(e){this.conversations.setCurrentId(e)}async getConversations(){const e=this.identity.getToken();return e?this.conversations.getConversations(e):(console.warn("[Crow] Cannot get conversations: user not identified"),[])}async loadHistory(e){const t=this.identity.getToken();return t?this.conversations.loadHistory(e,t):this.conversations.loadAnonymousHistory(e)}async switchConversation(e){const t=await this.loadHistory(e);this.conversations.setCurrentId(e),this.loadMessages(t)}async*sendMessage(e){var i,d,u,h,l,f,p,y,m,w;if(!e.trim())return;const t=this.generateMessageId("user");this.addMessage({id:t,content:e,role:"user",timestamp:new Date});const s=this.generateMessageId("assistant");this.addMessage({id:s,content:"",role:"assistant",timestamp:new Date}),this.setLoading(!0),this.abortController=new AbortController;let r="",n="";try{const c=await fetch(`${this.config.apiUrl}/api/chat/message`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({product_id:this.config.productId,message:e,conversation_id:this.conversations.getCurrentId(),identity_token:this.identity.getToken(),model:this.config.model,subdomain:this.config.subdomain,context:Object.keys(this.context).length>0?this.context:void 0}),signal:this.abortController.signal});if(!c.ok)throw new Error(`HTTP error: ${c.status}`);for await(const a of S(c,this.abortController.signal)){switch(a.type){case"content":r=a.accumulated,this.updateMessage(s,{content:r});break;case"thinking":n+=a.content,this.updateMessage(s,{thinking:n});break;case"thinking_complete":this.updateMessage(s,{thinkingComplete:!0});break;case"citations":this.updateMessage(s,{citations:a.citations});break;case"verification_status":this.identity.setVerified(a.isVerified);break;case"conversation_id":this.conversations.setCurrentId(a.conversationId);break;case"client_tool_call":await this.tools.execute(a.toolName,a.arguments),(d=(i=this.callbacks).onToolCall)==null||d.call(i,a);break;case"tool_call_start":case"tool_call_complete":(h=(u=this.callbacks).onToolCall)==null||h.call(u,a);break;case"workflow_started":case"workflow_todo_updated":case"workflow_ended":case"workflow_complete_prompt":(f=(l=this.callbacks).onWorkflow)==null||f.call(l,a);break;case"error":this.updateMessage(s,{content:a.message}),(y=(p=this.callbacks).onError)==null||y.call(p,new Error(a.message));break}yield a}}catch(c){if(c instanceof Error&&c.name==="AbortError"){r?this.updateMessage(s,{content:r}):(this._messages=this._messages.filter(a=>a.id!==s),this.notifyMessages());return}console.error("[Crow] Error:",c),this.updateMessage(s,{content:"Sorry, I encountered an error. Please try again."}),(w=(m=this.callbacks).onError)==null||w.call(m,c instanceof Error?c:new Error(String(c)))}finally{this.setLoading(!1),this.abortController=null}}async send(e){let t=null;for await(const r of this.sendMessage(e))if(r.type==="done")break;const s=this.messages;return s.length>0&&(t=s[s.length-1],t.role==="assistant")?t:null}stop(){this.abortController&&(this.abortController.abort(),this.setLoading(!1))}destroy(){this.stop(),this.messageListeners.clear(),this.loadingListeners.clear()}}function I(o,e,t){const s=o.find(n=>n.name.toLowerCase()===e.toLowerCase());if(!s)return null;let r=s.path;if(t)for(const[n,i]of Object.entries(t))r=r.replace(`:${n}`,String(i));return r}function O(o,e){return async t=>{try{const s=t.page,r=t.params,n=t.url;let i=null;if(s){if(i=I(o,s,r),!i)return{status:"error",error:`Unknown page: "${s}". Available pages: ${o.map(u=>u.name).join(", ")}`}}else if(n)i=n;else return{status:"error",error:'Either "page" or "url" parameter is required'};const d=i.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g);return d?{status:"error",error:`Missing parameters: ${d.join(", ")}. Please provide values for these parameters.`}:e?(e(i),{status:"success",data:{navigated_to:i,page:s||void 0,method:"spa_router"}}):(window.location.href=i,{status:"success",data:{navigated_to:i,page:s||void 0,method:"full_navigation"}})}catch(s){return{status:"error",error:String(s)}}}}exports.CrowBrowserUse=_.CrowBrowserUse;exports.WorkflowExecutor=_.WorkflowExecutor;exports.ConversationManager=C;exports.CrowClient=x;exports.DEFAULT_TOOLS=g;exports.DEFAULT_TOOL_NAMES=T;exports.IdentityManager=k;exports.ToolManager=v;exports.createNavigateToPageTool=O;exports.parseSSEChunk=M;exports.parseSSEData=b;exports.resolveRoute=I;exports.streamResponse=S;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index.native.cjs"),l=require("./workflowExecutor-D1pVvfZw.cjs");function c(s,i,t){const r=s.find(o=>o.name.toLowerCase()===i.toLowerCase());if(!r)return null;let n=r.path;if(t)for(const[o,a]of Object.entries(t))n=n.replace(`:${o}`,String(a));return n}function d(s,i){return async t=>{try{const r=t.page,n=t.params,o=t.url;let a=null;if(r){if(a=c(s,r,n),!a)return{status:"error",error:`Unknown page: "${r}". Available pages: ${s.map(g=>g.name).join(", ")}`}}else if(o)a=o;else return{status:"error",error:'Either "page" or "url" parameter is required'};const u=a.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g);return u?{status:"error",error:`Missing parameters: ${u.join(", ")}. Please provide values for these parameters.`}:i?(i(a),{status:"success",data:{navigated_to:a,page:r||void 0,method:"spa_router"}}):typeof window<"u"?(window.location.href=a,{status:"success",data:{navigated_to:a,page:r||void 0,method:"full_navigation"}}):{status:"error",error:"No navigateFn provided and window.location is unavailable (non-browser runtime). Pass a navigateFn to createNavigateToPageTool."}}catch(r){return{status:"error",error:String(r)}}}}exports.ConversationManager=e.ConversationManager;exports.CrowClient=e.CrowClient;exports.DEFAULT_TOOLS=e.DEFAULT_TOOLS;exports.DEFAULT_TOOL_NAMES=e.DEFAULT_TOOL_NAMES;exports.IdentityManager=e.IdentityManager;exports.ToolManager=e.ToolManager;exports.createLocalStorageAdapter=e.createLocalStorageAdapter;exports.createMemoryStorageAdapter=e.createMemoryStorageAdapter;exports.parseSSEChunk=e.parseSSEChunk;exports.parseSSEData=e.parseSSEData;exports.streamResponse=e.streamResponse;exports.CrowBrowserUse=l.CrowBrowserUse;exports.WorkflowExecutor=l.WorkflowExecutor;exports.createNavigateToPageTool=d;exports.resolveRoute=c;
package/dist/index.d.ts CHANGED
@@ -58,22 +58,23 @@ export declare interface Conversation {
58
58
  export declare class ConversationManager {
59
59
  private productId;
60
60
  private apiUrl;
61
+ private storage;
61
62
  private currentId;
62
- constructor(productId: string, apiUrl: string);
63
+ constructor(productId: string, apiUrl: string, storage: StorageAdapter);
63
64
  /**
64
- * Get localStorage key for this product
65
+ * Get storage key for this product
65
66
  */
66
67
  private getStorageKey;
67
68
  /**
68
- * Load conversation ID from localStorage
69
+ * Load conversation ID from storage
69
70
  */
70
71
  private loadFromStorage;
71
72
  /**
72
- * Save conversation ID to localStorage
73
+ * Save conversation ID to storage
73
74
  */
74
75
  private saveToStorage;
75
76
  /**
76
- * Clear conversation ID from localStorage
77
+ * Clear conversation ID from storage
77
78
  */
78
79
  private clearStorage;
79
80
  /**
@@ -114,6 +115,18 @@ export declare class ConversationManager {
114
115
  private parseContent;
115
116
  }
116
117
 
118
+ /**
119
+ * localStorage-backed adapter (default for browsers).
120
+ * Falls back to in-memory when localStorage is unavailable.
121
+ */
122
+ export declare function createLocalStorageAdapter(): StorageAdapter;
123
+
124
+ /**
125
+ * In-memory adapter. Useful for React Native, SSR, or tests.
126
+ * Data does not persist across sessions.
127
+ */
128
+ export declare function createMemoryStorageAdapter(): StorageAdapter;
129
+
117
130
  /**
118
131
  * Create a navigateToPage tool handler for the given routes.
119
132
  *
@@ -165,6 +178,7 @@ export declare class CrowClient {
165
178
  private identity;
166
179
  private tools;
167
180
  private conversations;
181
+ private readStream;
168
182
  private context;
169
183
  private abortController;
170
184
  private callbacks;
@@ -294,9 +308,6 @@ export declare class CrowClient {
294
308
  destroy(): void;
295
309
  }
296
310
 
297
- /**
298
- * Core types for @usecrow/client
299
- */
300
311
  export declare interface CrowClientConfig {
301
312
  /** Your Crow product ID from the dashboard */
302
313
  productId: string;
@@ -304,6 +315,22 @@ export declare interface CrowClientConfig {
304
315
  apiUrl?: string;
305
316
  /** Default model to use */
306
317
  model?: string;
318
+ /**
319
+ * Storage adapter for persisting conversation state.
320
+ * Defaults to localStorage (browser) or in-memory if unavailable.
321
+ */
322
+ storage?: StorageAdapter;
323
+ /**
324
+ * Custom stream reader for SSE responses.
325
+ * Override this for React Native or other non-browser runtimes
326
+ * where Response.body.getReader() is not available.
327
+ */
328
+ streamReader?: StreamReader;
329
+ /**
330
+ * Whether to register browser-only default tools (whatsOnScreen).
331
+ * Defaults to true in browser environments, false otherwise.
332
+ */
333
+ enableBrowserTools?: boolean;
307
334
  /** Subdomain for multi-endpoint products (routes to specific backend config) */
308
335
  subdomain?: string;
309
336
  }
@@ -336,9 +363,12 @@ export declare interface CrowEventCallbacks {
336
363
 
337
364
  export declare const DEFAULT_TOOL_NAMES: DefaultToolName[];
338
365
 
366
+ /**
367
+ * Browser default tools. Empty object in non-browser runtimes.
368
+ */
339
369
  export declare const DEFAULT_TOOLS: ToolHandlers;
340
370
 
341
- export declare type DefaultToolName = keyof typeof DEFAULT_TOOLS;
371
+ export declare type DefaultToolName = 'whatsOnScreen';
342
372
 
343
373
  declare namespace dom {
344
374
  export {
@@ -761,6 +791,16 @@ export declare interface StepResult {
761
791
  element_index?: number;
762
792
  }
763
793
 
794
+ /**
795
+ * Synchronous key-value storage interface.
796
+ * Matches the subset of Web Storage API used by ConversationManager.
797
+ */
798
+ export declare interface StorageAdapter {
799
+ getItem(key: string): string | null;
800
+ setItem(key: string, value: string): void;
801
+ removeItem(key: string): void;
802
+ }
803
+
764
804
  export declare type StreamEvent = {
765
805
  type: 'content';
766
806
  text: string;
@@ -834,6 +874,14 @@ export declare type StreamEvent = {
834
874
  action?: string;
835
875
  };
836
876
 
877
+ /**
878
+ * A function that reads SSE events from an HTTP Response.
879
+ *
880
+ * Browser default: uses Response.body.getReader() + TextDecoder.
881
+ * React Native: uses XMLHttpRequest onprogress or text-based parsing.
882
+ */
883
+ export declare type StreamReader = (response: Response, signal?: AbortSignal) => AsyncGenerator<StreamEvent>;
884
+
837
885
  /**
838
886
  * Create an async generator that streams events from a Response
839
887
  */