@erdoai/server 0.1.10 → 0.1.11

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.d.ts CHANGED
@@ -82,18 +82,37 @@ declare class ErdoClient {
82
82
  /**
83
83
  * List threads for the external user
84
84
  *
85
- * Requires token authentication with external_user_id.
86
- * Returns threads that the external user has access to via RBAC.
85
+ * Returns threads that the user has access to via RBAC.
87
86
  *
88
- * @example
87
+ * Supports two authentication patterns:
88
+ * - **Token auth**: Uses the external_user_id embedded in the token (recommended)
89
+ * - **API key auth**: Pass `externalUserId` to filter threads for that user
90
+ *
91
+ * ## Security Warning
92
+ *
93
+ * When using `externalUserId` with API key auth, you MUST determine the user ID
94
+ * from your own authentication system (session, JWT, etc.). **NEVER** accept
95
+ * `externalUserId` from client requests - this would allow users to access
96
+ * other users' threads.
97
+ *
98
+ * @example Token auth (B2B2C) - Recommended:
89
99
  * ```typescript
100
+ * // Token contains externalUserId - secure by design
90
101
  * const { threads } = await client.listThreads();
91
- * for (const thread of threads) {
92
- * console.log(thread.id, thread.name);
93
- * }
102
+ * ```
103
+ *
104
+ * @example API key auth with externalUserId (proxy pattern):
105
+ * ```typescript
106
+ * // SECURE: Get user ID from YOUR auth system, not from request
107
+ * const session = await getServerSession();
108
+ * const { threads } = await client.listThreads({
109
+ * externalUserId: session.user.id // From your auth, NOT from request body
110
+ * });
94
111
  * ```
95
112
  */
96
- listThreads(): Promise<ListThreadsResponse>;
113
+ listThreads(params?: {
114
+ externalUserId?: string;
115
+ }): Promise<ListThreadsResponse>;
97
116
  /**
98
117
  * Get a specific thread by ID
99
118
  *
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- var w="https://api.erdo.ai";function m(){let a=typeof process<"u"?process.env:{};return {endpoint:a.ERDO_ENDPOINT||w,authToken:a.ERDO_AUTH_TOKEN}}function g(a){let e=m(),t=a?.endpoint||e.endpoint||w,o=a?.authToken||e.authToken,n=a?.token;return {endpoint:t,authToken:o,token:n}}async function*l(a){if(!a.body)throw new Error("Response body is null");let e=a.body.getReader(),t=new TextDecoder,o="",n,s;try{for(;;){let{done:i,value:c}=await e.read();if(i){if(s!==void 0){let r=E(n,s);r&&(yield r);}break}o+=t.decode(c,{stream:!0});let u=o.split(`
2
- `);o=u.pop()||"";for(let r of u){let h=r.trim();if(!h){if(s!==void 0){let d=E(n,s);d&&(yield d),n=void 0,s=void 0;}continue}if(h.startsWith("event:"))n=h.slice(6).trim();else if(h.startsWith("data:")){let d=h.slice(5).trim();s===void 0?s=d:s+=`
3
- `+d;}}}}finally{e.releaseLock();}}function E(a,e){if(!e)return null;try{let t=JSON.parse(e);return a&&(t.type=a),t}catch{return {type:a,raw:e}}}async function f(a){let e=[];for await(let t of l(a))e.push(t);return e}var p=class{endpoint;authToken;token;constructor(e){let t=g(e);if(this.endpoint=t.endpoint,this.authToken=t.authToken,this.token=t.token,!this.authToken&&!this.token)throw new Error("Either authToken or token is required")}async createToken(e){if(!this.authToken)throw new Error("createToken requires authToken (API key) authentication");let t;if(e.botKeys&&e.botKeys.length>0){let c=`${this.endpoint}/resolve-bot-keys`,u=await fetch(c,{method:"POST",headers:{Authorization:`Bearer ${this.authToken}`,"Content-Type":"application/json"},body:JSON.stringify({keys:e.botKeys})});if(!u.ok){let d=await u.text();throw new Error(`Failed to resolve bot keys: ${u.status}: ${d}`)}let r=await u.json();t=Object.values(r.bots);let h=e.botKeys.filter(d=>!r.bots[d]);if(h.length>0)throw new Error(`Bot keys not found or access denied: ${h.join(", ")}`)}let o=`${this.endpoint}/tokens`,n={};t&&(n.bot_ids=t),e.datasetIds&&(n.dataset_ids=e.datasetIds),e.threadIds&&(n.thread_ids=e.threadIds),e.externalUserId&&(n.external_user_id=e.externalUserId),e.expiresInSeconds&&(n.expires_in_seconds=e.expiresInSeconds);let s=await fetch(o,{method:"POST",headers:{Authorization:`Bearer ${this.authToken}`,"Content-Type":"application/json"},body:JSON.stringify(n)});if(!s.ok){let c=await s.text();throw new Error(`Failed to create token: ${s.status}: ${c}`)}let i=await s.json();return {tokenId:i.token_id,token:i.token,expiresAt:i.expires_at}}async createThread(e={}){if(!this.token)throw new Error("createThread requires token authentication");let t=`${this.endpoint}/threads`,o={};e.name&&(o.name=e.name),e.datasetIds&&(o.dataset_ids=e.datasetIds);let n=await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json"},body:JSON.stringify(o)});if(!n.ok){let i=await n.text();throw new Error(`Failed to create thread: ${n.status}: ${i}`)}let s=await n.json();return {id:s.id,name:s.name,createdAt:s.created_at,updatedAt:s.updated_at}}async listThreads(){if(!this.token)throw new Error("listThreads requires token authentication");let e=`${this.endpoint}/threads-user`,t=await fetch(e,{method:"GET",headers:{Authorization:`Bearer ${this.token}`}});if(!t.ok){let n=await t.text();throw new Error(`Failed to list threads: ${t.status}: ${n}`)}return {threads:(await t.json()).threads.map(n=>({id:n.id,name:n.name,createdAt:n.created_at,updatedAt:n.updated_at}))}}async getThread(e){if(!this.token)throw new Error("getThread requires token authentication");let t=`${this.endpoint}/threads/${e}`,o=await fetch(t,{method:"GET",headers:{Authorization:`Bearer ${this.token}`}});if(!o.ok){let s=await o.text();throw new Error(`Failed to get thread: ${o.status}: ${s}`)}let n=await o.json();return {id:n.id,name:n.name,createdAt:n.created_at,updatedAt:n.updated_at}}async getThreadMessages(e){if(!this.token)throw new Error("getThreadMessages requires token authentication");let t=`${this.endpoint}/threads/${e}/messages`,o=await fetch(t,{method:"GET",headers:{Authorization:`Bearer ${this.token}`}});if(!o.ok){let s=await o.text();throw new Error(`Failed to get thread messages: ${o.status}: ${s}`)}return {messages:(await o.json()).messages.map(s=>({id:s.id,thread_id:s.thread_id,role:s.role,created_at:s.created_at,updated_at:s.updated_at,contents:s.contents.map(i=>({id:i.id,content_type:i.content_type,ui_content_type:i.ui_content_type,content:i.content,user_visibility:i.user_visibility,bot_visibility:i.bot_visibility,created_at:i.created_at}))}))}}async*sendMessage(e,t){if(!this.token)throw new Error("sendMessage requires token authentication");let o=`${this.endpoint}/threads/${e}/message`,n={content:t.content};t.botKey&&(n.bot_key=t.botKey);let s=await fetch(o,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n)});if(!s.ok){let i=await s.text();throw new Error(`Failed to send message: ${s.status}: ${i}`)}yield*l(s);}async sendMessageAndWait(e,t){if(!this.token)throw new Error("sendMessageAndWait requires token authentication");let o=`${this.endpoint}/threads/${e}/message`,n={content:t.content};t.botKey&&(n.bot_key=t.botKey);let s=await fetch(o,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n)});if(!s.ok){let i=await s.text();throw new Error(`Failed to send message: ${s.status}: ${i}`)}return f(s)}async invoke(e,t={}){let o=await this.collectEvents(e,t);return this.extractResult(e,o)}async*invokeStream(e,t={}){let o=await this.makeRequest(e,t);yield*l(o);}async collectEvents(e,t){let o=await this.makeRequest(e,t);return f(o)}async makeRequest(e,t){let o=`${this.endpoint}/bots/${e}/invoke`,n={};t.messages&&(n.messages=t.messages),t.parameters&&(n.parameters=t.parameters),t.datasets&&(n.dataset_slugs=t.datasets),t.mode&&(n.mode=t.mode),t.manualMocks&&(n.manual_mocks=t.manualMocks);let s=this.token||this.authToken,i=await fetch(o,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n)});if(!i.ok){let c=await i.text();throw new Error(`API request failed with status ${i.status}: ${c}`)}return i}extractResult(e,t){let o=[],n=[],s=new Set,i,c;for(let u of t){if(!u)continue;let r=u.payload,h=u.metadata;if(r&&typeof r=="object"&&"invocation_id"in r&&!c&&(c=r.invocation_id),r&&typeof r=="object"&&"action_type"in r&&"key"in r){let d=r.key;s.has(d)||(s.add(d),n.push({key:d,action:r.action_type,status:"completed"}));}if(r&&typeof r=="object"&&"output"in r&&h?.user_visibility==="visible"){let d=r.output;if(d&&typeof d=="object"&&"content"in d){let y=d.content;if(Array.isArray(y)){for(let v of y)if(v.content_type==="text"){let S=h?.role||"assistant";o.push({role:S,content:v.content||""});}}}}r&&typeof r=="object"&&"status"in r&&"output"in r&&(i={status:r.status,parameters:r.parameters,output:r.output,message:r.message,error:r.error});}return {success:true,botKey:e,invocationId:c,result:i,messages:o,events:t,steps:n}}},k=null;function T(){return k||(k=new p),k}async function _(a,e={}){return T().invoke(a,e)}async function*b(a,e={}){yield*T().invokeStream(a,e);}export{p as ErdoClient,f as collectSSEEvents,m as getConfigFromEnv,_ as invoke,b as invokeStream,l as parseSSEStream,g as resolveConfig};
1
+ var v="https://api.erdo.ai";function E(){let a=typeof process<"u"?process.env:{};return {endpoint:a.ERDO_ENDPOINT||v,authToken:a.ERDO_AUTH_TOKEN}}function k(a){let e=E(),t=a?.endpoint||e.endpoint||v,o=a?.authToken||e.authToken,n=a?.token;return {endpoint:t,authToken:o,token:n}}async function*f(a){if(!a.body)throw new Error("Response body is null");let e=a.body.getReader(),t=new TextDecoder,o="",n,s;try{for(;;){let{done:r,value:c}=await e.read();if(r){if(s!==void 0){let i=S(n,s);i&&(yield i);}break}o+=t.decode(c,{stream:!0});let u=o.split(`
2
+ `);o=u.pop()||"";for(let i of u){let h=i.trim();if(!h){if(s!==void 0){let d=S(n,s);d&&(yield d),n=void 0,s=void 0;}continue}if(h.startsWith("event:"))n=h.slice(6).trim();else if(h.startsWith("data:")){let d=h.slice(5).trim();s===void 0?s=d:s+=`
3
+ `+d;}}}}finally{e.releaseLock();}}function S(a,e){if(!e)return null;try{let t=JSON.parse(e);return a&&(t.type=a),t}catch{return {type:a,raw:e}}}async function l(a){let e=[];for await(let t of f(a))e.push(t);return e}function p(a){if(typeof a=="string")return a;if(a&&typeof a=="object"&&"String"in a&&"Valid"in a&&a.Valid)return a.String}var g=class{endpoint;authToken;token;constructor(e){let t=k(e);if(this.endpoint=t.endpoint,this.authToken=t.authToken,this.token=t.token,!this.authToken&&!this.token)throw new Error("Either authToken or token is required")}async createToken(e){if(!this.authToken)throw new Error("createToken requires authToken (API key) authentication");let t;if(e.botKeys&&e.botKeys.length>0){let c=`${this.endpoint}/resolve-bot-keys`,u=await fetch(c,{method:"POST",headers:{Authorization:`Bearer ${this.authToken}`,"Content-Type":"application/json"},body:JSON.stringify({keys:e.botKeys})});if(!u.ok){let d=await u.text();throw new Error(`Failed to resolve bot keys: ${u.status}: ${d}`)}let i=await u.json();t=Object.values(i.bots);let h=e.botKeys.filter(d=>!i.bots[d]);if(h.length>0)throw new Error(`Bot keys not found or access denied: ${h.join(", ")}`)}let o=`${this.endpoint}/tokens`,n={};t&&(n.bot_ids=t),e.datasetIds&&(n.dataset_ids=e.datasetIds),e.threadIds&&(n.thread_ids=e.threadIds),e.externalUserId&&(n.external_user_id=e.externalUserId),e.expiresInSeconds&&(n.expires_in_seconds=e.expiresInSeconds);let s=await fetch(o,{method:"POST",headers:{Authorization:`Bearer ${this.authToken}`,"Content-Type":"application/json"},body:JSON.stringify(n)});if(!s.ok){let c=await s.text();throw new Error(`Failed to create token: ${s.status}: ${c}`)}let r=await s.json();return {tokenId:r.token_id,token:r.token,expiresAt:r.expires_at}}async createThread(e={}){if(!this.token)throw new Error("createThread requires token authentication");let t=`${this.endpoint}/threads`,o={};e.name&&(o.name=e.name),e.datasetIds&&(o.dataset_ids=e.datasetIds);let n=await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json"},body:JSON.stringify(o)});if(!n.ok){let r=await n.text();throw new Error(`Failed to create thread: ${n.status}: ${r}`)}let s=await n.json();return {id:s.id,name:p(s.name)||"",createdAt:s.created_at,updatedAt:s.updated_at}}async listThreads(e){if(!this.token&&!this.authToken)throw new Error("listThreads requires authentication (token or authToken)");let t=`${this.endpoint}/threads-user`;e?.externalUserId&&(t+=`?external_user_id=${encodeURIComponent(e.externalUserId)}`);let o=this.token?`Bearer ${this.token}`:`Bearer ${this.authToken}`,n=await fetch(t,{method:"GET",headers:{Authorization:o}});if(!n.ok){let r=await n.text();throw new Error(`Failed to list threads: ${n.status}: ${r}`)}return {threads:(await n.json()).threads.map(r=>({id:r.id,name:p(r.name)||"",createdAt:r.created_at,updatedAt:r.updated_at}))}}async getThread(e){if(!this.token)throw new Error("getThread requires token authentication");let t=`${this.endpoint}/threads/${e}`,o=await fetch(t,{method:"GET",headers:{Authorization:`Bearer ${this.token}`}});if(!o.ok){let s=await o.text();throw new Error(`Failed to get thread: ${o.status}: ${s}`)}let n=await o.json();return {id:n.id,name:p(n.name)||"",createdAt:n.created_at,updatedAt:n.updated_at}}async getThreadMessages(e){if(!this.token)throw new Error("getThreadMessages requires token authentication");let t=`${this.endpoint}/threads/${e}/messages`,o=await fetch(t,{method:"GET",headers:{Authorization:`Bearer ${this.token}`}});if(!o.ok){let s=await o.text();throw new Error(`Failed to get thread messages: ${o.status}: ${s}`)}return {messages:(await o.json()).messages.map(s=>({id:s.message.id,thread_id:s.message.thread_id,role:s.message.author_entity_type==="user"?"user":"assistant",created_at:s.message.created_at,updated_at:s.message.updated_at,contents:s.contents.map(r=>({id:r.id,content_type:r.content_type,ui_content_type:p(r.ui_content_type),content:r.content,user_visibility:r.user_visibility,bot_visibility:r.bot_visibility,created_at:r.created_at}))}))}}async*sendMessage(e,t){if(!this.token)throw new Error("sendMessage requires token authentication");let o=`${this.endpoint}/threads/${e}/message`,n={content:t.content};t.botKey&&(n.bot_key=t.botKey);let s=await fetch(o,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n)});if(!s.ok){let r=await s.text();throw new Error(`Failed to send message: ${s.status}: ${r}`)}yield*f(s);}async sendMessageAndWait(e,t){if(!this.token)throw new Error("sendMessageAndWait requires token authentication");let o=`${this.endpoint}/threads/${e}/message`,n={content:t.content};t.botKey&&(n.bot_key=t.botKey);let s=await fetch(o,{method:"POST",headers:{Authorization:`Bearer ${this.token}`,"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n)});if(!s.ok){let r=await s.text();throw new Error(`Failed to send message: ${s.status}: ${r}`)}return l(s)}async invoke(e,t={}){let o=await this.collectEvents(e,t);return this.extractResult(e,o)}async*invokeStream(e,t={}){let o=await this.makeRequest(e,t);yield*f(o);}async collectEvents(e,t){let o=await this.makeRequest(e,t);return l(o)}async makeRequest(e,t){let o=`${this.endpoint}/bots/${e}/invoke`,n={};t.messages&&(n.messages=t.messages),t.parameters&&(n.parameters=t.parameters),t.datasets&&(n.dataset_slugs=t.datasets),t.mode&&(n.mode=t.mode),t.manualMocks&&(n.manual_mocks=t.manualMocks);let s=this.token||this.authToken,r=await fetch(o,{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n)});if(!r.ok){let c=await r.text();throw new Error(`API request failed with status ${r.status}: ${c}`)}return r}extractResult(e,t){let o=[],n=[],s=new Set,r,c;for(let u of t){if(!u)continue;let i=u.payload,h=u.metadata;if(i&&typeof i=="object"&&"invocation_id"in i&&!c&&(c=i.invocation_id),i&&typeof i=="object"&&"action_type"in i&&"key"in i){let d=i.key;s.has(d)||(s.add(d),n.push({key:d,action:i.action_type,status:"completed"}));}if(i&&typeof i=="object"&&"output"in i&&h?.user_visibility==="visible"){let d=i.output;if(d&&typeof d=="object"&&"content"in d){let w=d.content;if(Array.isArray(w)){for(let m of w)if(m.content_type==="text"){let _=h?.role||"assistant";o.push({role:_,content:m.content||""});}}}}i&&typeof i=="object"&&"status"in i&&"output"in i&&(r={status:i.status,parameters:i.parameters,output:i.output,message:i.message,error:i.error});}return {success:true,botKey:e,invocationId:c,result:r,messages:o,events:t,steps:n}}},y=null;function T(){return y||(y=new g),y}async function b(a,e={}){return T().invoke(a,e)}async function*x(a,e={}){yield*T().invokeStream(a,e);}export{g as ErdoClient,l as collectSSEEvents,E as getConfigFromEnv,b as invoke,x as invokeStream,f as parseSSEStream,k as resolveConfig};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@erdoai/server",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "Erdo server SDK for invoking agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -40,7 +40,7 @@
40
40
  "directory": "packages/server"
41
41
  },
42
42
  "dependencies": {
43
- "@erdoai/types": "^0.1.9"
43
+ "@erdoai/types": "^0.1.10"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/node": "^24.10.1",