broods 0.7.0 → 0.8.1
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/account.d.ts +6 -0
- package/dist/{chunk-jrshks26.js → chunk-phmt1gk7.js} +5 -5
- package/dist/cli/index.js +90 -99
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1 -1
- package/package.json +5 -5
package/dist/account.d.ts
CHANGED
|
@@ -262,6 +262,7 @@ interface AgentTelegramChannelConfig {
|
|
|
262
262
|
webhookSecret?: string;
|
|
263
263
|
allowedChatIds?: number[];
|
|
264
264
|
reactionEmoji?: string;
|
|
265
|
+
trace?: "enabled" | "disabled";
|
|
265
266
|
workspaceScope?: AgentChannelWorkspaceScope;
|
|
266
267
|
[key: string]: unknown;
|
|
267
268
|
}
|
|
@@ -280,6 +281,7 @@ interface AgentGitHubChannelConfig {
|
|
|
280
281
|
triggerOnIssueOpen?: boolean;
|
|
281
282
|
/** When false, the bot does not auto-trigger on new PRs (opened/edited/reopened). Defaults to true. The bot still triggers when assigned to a PR. */
|
|
282
283
|
triggerOnPROpen?: boolean;
|
|
284
|
+
trace?: "enabled" | "disabled";
|
|
283
285
|
workspaceScope?: AgentChannelWorkspaceScope;
|
|
284
286
|
[key: string]: unknown;
|
|
285
287
|
}
|
|
@@ -290,6 +292,7 @@ interface AgentSlackChannelConfig {
|
|
|
290
292
|
signingSecret?: string;
|
|
291
293
|
allowedChannelIds?: string[];
|
|
292
294
|
reactionEmoji?: string;
|
|
295
|
+
trace?: "enabled" | "disabled";
|
|
293
296
|
workspaceScope?: AgentChannelWorkspaceScope;
|
|
294
297
|
[key: string]: unknown;
|
|
295
298
|
}
|
|
@@ -303,6 +306,7 @@ interface AgentDiscordChannelConfig {
|
|
|
303
306
|
botUserId?: string;
|
|
304
307
|
/** Role ids that count as mentioning the agent, e.g. an on-call role. */
|
|
305
308
|
mentionRoleIds?: string[];
|
|
309
|
+
trace?: "enabled" | "disabled";
|
|
306
310
|
workspaceScope?: AgentChannelWorkspaceScope;
|
|
307
311
|
[key: string]: unknown;
|
|
308
312
|
}
|
|
@@ -312,6 +316,7 @@ interface AgentPancakeChannelConfig {
|
|
|
312
316
|
pageAccessToken?: string;
|
|
313
317
|
webhookSecret?: string;
|
|
314
318
|
senderId?: string;
|
|
319
|
+
trace?: "enabled" | "disabled";
|
|
315
320
|
workspaceScope?: AgentChannelWorkspaceScope;
|
|
316
321
|
[key: string]: unknown;
|
|
317
322
|
}
|
|
@@ -320,6 +325,7 @@ interface AgentZaloChannelConfig {
|
|
|
320
325
|
botToken?: string;
|
|
321
326
|
webhookSecret?: string;
|
|
322
327
|
allowedUserIds?: string[];
|
|
328
|
+
trace?: "enabled" | "disabled";
|
|
323
329
|
workspaceScope?: AgentChannelWorkspaceScope;
|
|
324
330
|
[key: string]: unknown;
|
|
325
331
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
function b(e){if(e.events&&e.input!==void 0)throw Error("Run input accepts either `input` or `events`, not both");if(e.events&&e.events.length>0)return e.events;if(typeof e.input==="string")return[{role:"user",content:[{type:"text",text:e.input}]}];throw Error("Run input requires `input` (string) or a non-empty `events` array")}async function*
|
|
2
|
-
`);o=i.pop()??"";for(let a of i)if(a.startsWith("data: "))yield a.slice(6)}}finally{n.releaseLock()}}import{mkdir as F,readFile as Z,writeFile as z}from"node:fs/promises";import{homedir as V}from"node:os";import{dirname as Y,join as X}from"node:path";var
|
|
3
|
-
`,"utf8")}function g(e){let n=e.length;while(n>0&&e.charCodeAt(n-1)===47)n-=1;return e.slice(0,n)}function Te(e){try{let n=new URL(e);if(!n.hostname.startsWith(`${D}.`))return;let t=n.hostname.slice(D.length+1);if(t!==E&&!t.endsWith(`.${E}`))return;return n.hostname=`${Q}.${t}`,g(n.origin)}catch{return}}import{existsSync as ee,readFileSync as N,statSync as ne}from"node:fs";import{join as te,resolve as oe}from"node:path";var
|
|
4
|
-
`).replace(/\\"/g,'"');if(e.startsWith("'")&&e.endsWith("'"))return e.slice(1,-1);let n=e.indexOf(" #");return n>=0?e.slice(0,n).trimEnd():e}function ce(){try{let e=JSON.parse(N(m,"utf8"));if(typeof e.baseUrl!=="string"||typeof e.token!=="string")return null;return{baseUrl:g(e.baseUrl),...typeof e.dashboardUrl==="string"?{dashboardUrl:g(e.dashboardUrl)}:{},token:e.token}}catch{return null}}var A="https://gateway.broods.app";class U extends Error{accepted;constructor(e){super(`Ingress ${e.eventId} was accepted asynchronously with status ${e.status??"queued"}`);this.accepted=e;this.name="IngressAcceptedError"}}class le{baseUrl;apiKey;fetchImpl;constructor(e={}){k(),this.baseUrl=w(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||A),this.apiKey=e.apiKey||process.env.BROODS_API_KEY||void 0,this.fetchImpl=e.fetch??fetch}channelWebhookUrl(e){return`${this.baseUrl}${e.webhookPath.startsWith("/")?"":"/"}${e.webhookPath}`}accountWebhookUrl(e,n){let t=[e,n].map(encodeURIComponent);return`${this.baseUrl}/webhooks/${t.join("/")}`}agent(e,n){if(typeof e==="string"){let o=e,r=n??"";if(!r)throw Error(`Agent ${o} is missing a generated id. Run broods dev --once or broods deploy to sync it first.`);return{id:r,run:(s)=>this.run({...s,agentName:o,agentId:r}),runAsync:(s)=>this.runAsync({...s,agentName:o,agentId:r}),stream:(s)=>this.stream({...s,agentName:o,agentId:r})}}let t=e;if(!t.id)throw Error(`Agent ${t.name} is missing a generated id. Run broods dev --once or broods deploy to sync it first.`);return{id:t.id,run:(o)=>this.run(t,o),runAsync:(o)=>this.runAsync(t,o),stream:(o)=>this.stream(t,o)}}async run(e,n){let t=[],o="",r=n?this.stream(e,n):this.stream(e);for await(let s of r)if(t.push(s),s.type==="text-delta")o+=s.text;return{text:o,events:t}}async*stream(e,n){let t=n?{...n,agentId:e.id,agentName:e.name}:e,o=P(t,"cli"),r=n?this.scopedUrl(e):this.baseUrl,s=await this.openStream(o,r);if(!s.ok)throw Error(`Run failed: ${s.status} ${await s.text()}`);if(s.status===202)throw new U(O(await s.json(),o));if(!s.body)throw Error("Run response has no body");for await(let i of I(s.body)){let a;try{a=JSON.parse(i)}catch{continue}if(a.type==="error")throw Error(`Agent run failed: ${ye(a.error)}`);yield a}}async runAsync(e,n){let t=n?{...n,agentId:e.id,agentName:e.name}:e,o=P(t,"async"),r=n?this.scopedUrl(e,"/async"):`${this.baseUrl}/async`,s=await this.fetchJson(r,{method:"POST",headers:this.apiKeyHeaders(),body:JSON.stringify(o)});if(s.status!==202)throw Error(`Async run failed: ${s.status} ${await ue(s,"202 JSON")}`);let i=O(await s.json(),o);return{...i,conversationKey:o.conversationKey,poll:()=>this.getAsyncStatus(i),wait:(a)=>this.waitForAsyncStatus(i,a)}}async getAsyncStatus(e,n={}){let t=this.resolveStatusUrl(e,n),o=await this.fetchJson(t,{method:"GET",headers:this.apiKeyHeaders()});if(o.status===404)return{status:"not_found"};if(!o.ok)throw Error(`Status check failed: ${o.status} ${await o.text()}`);return await o.json()}async waitForAsyncStatus(e,n={}){let t=Date.now()+(n.timeoutMs??180000),o=n.intervalMs??2000;while(Date.now()<t){if(n.signal?.aborted)throw Error("Async status polling aborted.");let r=await this.getAsyncStatus(e,n);if(r.status==="awaiting_approval"||r.status==="completed"||r.status==="failed"||r.status==="expired"||r.status==="not_found")return r;await fe(o,n.signal)}throw Error("Polling timeout")}async createCron(e){let n=await this.fetchJson(`${this.baseUrl}/v1/crons`,{method:"POST",headers:this.apiKeyHeaders(),body:JSON.stringify(pe(e))});if(n.status!==201)throw Error(`Create cron job failed: ${n.status} ${await p(n)}`);return await n.json()}async listCrons(){let e=await this.fetchJson(`${this.baseUrl}/v1/crons`,{method:"GET",headers:this.apiKeyHeaders()});if(!e.ok)throw Error(`List cron jobs failed: ${e.status} ${await p(e)}`);return(await e.json()).crons}async getCron(e){let n=await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(e)}`,{method:"GET",headers:this.apiKeyHeaders()});if(n.status===404)return null;if(!n.ok)throw Error(`Get cron job failed: ${n.status} ${await p(n)}`);return await n.json()}async listCronRuns(e,n={}){let t=new URLSearchParams;if(n.limit!==void 0)t.set("limit",String(n.limit));let o=t.size>0?`?${t}`:"",r=await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(e)}/runs${o}`,{method:"GET",headers:this.apiKeyHeaders()});if(!r.ok)throw Error(`List cron job runs failed: ${r.status} ${await p(r)}`);return(await r.json()).runs}async updateCron(e,n){let t=await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(e)}`,{method:"PATCH",headers:this.apiKeyHeaders(),body:JSON.stringify(n)});if(!t.ok)throw Error(`Update cron job failed: ${t.status} ${await p(t)}`);return await t.json()}async deleteCron(e){let n=await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(e)}`,{method:"DELETE",headers:this.apiKeyHeaders()});if(n.status===404)return!1;if(!n.ok)throw Error(`Delete cron job failed: ${n.status} ${await p(n)}`);return(await n.json()).deleted}scopedUrl(e,n=""){if(e.projectSlug&&e.environmentSlug&&e.endpointId)return`${this.baseUrl}/v1/${encodeURIComponent(e.projectSlug)}/agents/${encodeURIComponent(e.environmentSlug)}/${encodeURIComponent(e.endpointId)}${n}`;return`${this.baseUrl}${n}`}async openStream(e,n){if(this.apiKey)return await this.fetchCore(e,n,{Authorization:`Bearer ${this.apiKey}`});throw Error(`BroodsClient streams directly from the core service at ${this.baseUrl}. Provide apiKey. For a self-hosted core service, set host/baseUrl or BROODS_HOST/BROODS_BASE_URL.`)}async fetchCore(e,n,t){try{return await this.fetchImpl(n,{method:"POST",headers:{"Content-Type":"application/json",Accept:"text/event-stream",...t},body:JSON.stringify(e)})}catch(o){throw Error(`Cannot access the broods core service at ${n}. The SDK uses ${A} by default; set host/baseUrl or BROODS_HOST/BROODS_BASE_URL to your own core service URL if your account uses a custom deployment. Cause: ${o instanceof Error?o.message:String(o)}`)}}apiKeyHeaders(){if(!this.apiKey)throw Error("BroodsClient requires apiKey or BROODS_API_KEY.");return{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`}}async fetchJson(e,n){try{return await this.fetchImpl(e,{...n,headers:{"Content-Type":"application/json",...n.headers??{}}})}catch(t){throw Error(`Cannot access the broods core service at ${e}. Cause: ${t instanceof Error?t.message:String(t)}`)}}resolveStatusUrl(e,n){if(typeof e!=="string")return e.statusUrl;if(/^https?:\/\//.test(e))return e;if(!n.agentId)throw Error("Polling by status id requires agentId.");return de(this.baseUrl,e,n.agentId)}}function w(e){let n=e.trim(),t=/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(n)?n:`https://${n}`;return g(t)}function P(e,n){let t=e.eventId??`${n}-${Date.now()}`;return{agentId:e.agentId,eventId:t,conversationKey:e.conversationKey??t,events:b(e),...e.mode!==void 0?{mode:e.mode}:{},...e.idempotencyKey!==void 0?{idempotencyKey:e.idempotencyKey}:{},...e.system!==void 0?{system:e.system}:{},...e.model!==void 0?{model:e.model}:{}}}function O(e,n){if(!e||typeof e!=="object")throw Error("Async response must be an object");let t=e.statusUrl;if(typeof t!=="string"||t.length===0)throw Error("Async response missing statusUrl");let o=ge(t);if(!o.statusId)throw Error("Async response statusUrl missing status id");let r=typeof e.eventId==="string"?e.eventId:o.statusId,s=e.status,i=e.requestedMode;return{statusUrl:t,statusId:o.statusId,eventId:r,agentId:o.agentId??n.agentId,...s==="accepted"||s==="queued"||s==="applied"||s==="processing"?{status:s}:{},...i==="reject"||i==="followup"||i==="collect"||i==="steer"?{requestedMode:i}:{}}}function ge(e){let n=new URL(e),t=n.pathname.match(/\/status\/([^/]+)$/);return{statusId:t?.[1]?decodeURIComponent(t[1]):void 0,agentId:n.searchParams.get("agentId")??void 0}}function de(e,n,t){return`${w(e)}/status/${encodeURIComponent(n)}?agentId=${encodeURIComponent(t)}`}async function ue(e,n){if((e.headers.get("content-type")??"").toLowerCase().includes("text/event-stream"))return await e.body?.cancel().catch(()=>{}),`expected ${n}, but the server returned an SSE stream. This usually means the core deployment routed /async to the direct streaming runner instead of the async handler.`;let o=await e.text();if(o.length<=2000)return o;return`${o.slice(0,2000)}... [truncated ${o.length-2000} chars]`}async function p(e){let n=await e.text();if(n.includes("Request body must include eventId and conversationKey"))return`${n}. Cron job APIs must be served by the configured baseUrl. Prefer defining stable cron jobs with defineCron(...) in broods/ and syncing with \`broods dev\` or \`broods deploy\`.`;return n}function pe(e){if("agentId"in e)return e;let n=e.agent,t=typeof n==="string"?n:n.id,{agent:o,...r}=e;return{...r,agentId:t}}function fe(e,n){return new Promise((t,o)=>{if(n?.aborted){o(Error("Async status polling aborted."));return}let r=setTimeout(t,e);n?.addEventListener("abort",()=>{clearTimeout(r),o(Error("Async status polling aborted."))},{once:!0})})}function ye(e){if(typeof e==="string")return e;if(!e||typeof e!=="object")return String(e);let n=e,t=n.data?.error?.message??n.message??n.responseBody??JSON.stringify(e),o=n.name?`${n.name}: `:"",r=n.statusCode?` (HTTP ${n.statusCode})`:"";return`${o}${t}${r}`}var me=2000,W=0,h=1;class he{baseUrl;apiKey;WebSocketImpl;connectTimeoutMs;constructor(e={}){if(k(),this.baseUrl=B(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||A),this.apiKey=e.apiKey||process.env.BROODS_API_KEY||"",!this.apiKey)throw Error("BroodsWebSocketClient requires apiKey or BROODS_API_KEY.");this.WebSocketImpl=e.WebSocket,this.connectTimeoutMs=e.connectTimeoutMs??me}subscribe(e,n={}){let t=this.resolveWebSocket(),o=this.buildUrl(e),r=Se(e),s=M(this.baseUrl),i=new t(o),a=!1,l=!1,d=setTimeout(()=>{if(!a)y(s)},this.connectTimeoutMs),v=(c=1000,R="client closed")=>{if(l)return;if(l=!0,clearTimeout(d),e.signal?.removeEventListener("abort",S),i.readyState===h||i.readyState===W)i.close(c,R)},y=(c)=>{n.onError?.(c),v(1011,"websocket error")},J=()=>{n.onDone?.(),v(1000,"done")},S=()=>{if(i.readyState===h)i.send(JSON.stringify({type:"cancel"}));y(Error("WebSocket run aborted."))};return e.signal?.addEventListener("abort",S,{once:!0}),i.onopen=()=>{if(a=!0,clearTimeout(d),e.signal?.aborted){S();return}i.send(JSON.stringify({type:"execute",agentId:r,events:b(e),sessionId:e.sessionId,...e.eventId!==void 0?{eventId:e.eventId}:{},...e.mode!==void 0?{mode:e.mode}:{},...e.idempotencyKey!==void 0?{idempotencyKey:e.idempotencyKey}:{},...e.system!==void 0?{system:e.system}:{},...e.model!==void 0?{model:e.model}:{}}))},i.onmessage=(c)=>{if(typeof c.data!=="string")return;let R=K(c.data);if(!R)return;let C=_(R,n);switch(n.onMessage?.(C),C.type){case"meta":n.onMeta?.(C);break;case"done":J();break;case"error":y(Error(H(C.error)));break}},i.onerror=()=>{y(s)},i.onclose=(c)=>{if(clearTimeout(d),e.signal?.removeEventListener("abort",S),l||c.code===1000)return;y(Error(c.reason||"WebSocket connection closed."))},{url:o,sendControl(c){if(i.readyState!==h)throw Error("WebSocket is not open");i.send(JSON.stringify({type:"control",...c}))},close:v}}attach(e,n={}){let t=this.resolveWebSocket(),o=this.buildUrl(e),r=new t(o),s=!1,i=(a=1000,l="client closed")=>{if(s)return;if(s=!0,r.readyState===h||r.readyState===W)r.close(a,l)};return r.onopen=()=>r.send(JSON.stringify({type:"attach",...e,signal:void 0,agent:void 0})),r.onmessage=(a)=>{if(typeof a.data!=="string")return;let l=K(a.data);if(!l)return;let d=_(l,n);if(n.onMessage?.(d),d.type==="done")n.onDone?.();if(d.type==="error")n.onError?.(Error(H(d.error)))},r.onerror=()=>n.onError?.(M(this.baseUrl)),r.onclose=(a)=>{if(!s&&a.code!==1000)n.onError?.(Error(a.reason||"WebSocket connection closed."))},{url:o,sendControl(a){if(r.readyState!==h)throw Error("WebSocket is not open");r.send(JSON.stringify({type:"control",...a}))},close:i}}async*stream(e){let n=[],t=!1,o=null,r=null,s=()=>{r?.(),r=null},i=this.subscribe(e,{onMessage(a){n.push(a),s()},onDone(){t=!0,s()},onError(a){o=a,s()}});try{while(!0){if(n.length>0){yield n.shift();continue}if(o)throw o;if(t)return;await new Promise((a)=>{r=a})}}finally{i.close()}}buildUrl(e){let n=Re(e),t=e.projectSlug??e.agent?.projectSlug,o=e.environmentSlug??e.agent?.environmentSlug,r=t?`/${t}`:"",s=o?`/${o}`:"";return`${Ce(this.baseUrl)}/v1${r}/agents${s}/${encodeURIComponent(n)}/ws?token=${encodeURIComponent(this.apiKey)}`}resolveWebSocket(){let e=this.WebSocketImpl??globalThis.WebSocket;if(!e)throw Error("WebSocket is not available. Pass a WebSocket implementation in BroodsWebSocketClient options.");return e}}function Se(e){let n=e.agentId??e.agent?.id??e.endpointId;if(!n)throw Error("WebSocket run requires agent, agentId, or endpointId.");return n}function Re(e){let n=e.endpointId??e.agent?.endpointId??e.agent?.id;if(!n)throw Error("WebSocket run requires agent or endpointId.");return n}function Ce(e){let n=new URL(B(e));if(n.protocol==="https:")n.protocol="wss:";if(n.protocol==="http:")n.protocol="ws:";return g(n.toString())}function B(e){let n=e.trim();if(/^wss?:\/\//.test(n))return g(n);return w(n)}function M(e){return Error(`Cannot access the WebSocket service at ${e}.`)}function _(e,n){if(e.type!=="output")return e;let t=e;return n.onOutput?.(t),t.data}function K(e){try{let n=JSON.parse(e);return typeof n==="object"&&n!==null&&typeof n.type==="string"?n:null}catch{return null}}function H(e){if(typeof e==="string")return e;if(e&&typeof e==="object"&&typeof e.message==="string")return e.message;return e===void 0?"WebSocket stream error.":JSON.stringify(e)}var L=Symbol.for("broods.resource"),q=Symbol.for("broods.config"),G=Symbol.for("broods.channel"),j=/^[A-Z][A-Z0-9_]{0,63}$/;var Je=new Proxy((e)=>{if(!j.test(e))throw Error("env name must match /^[A-Z][A-Z0-9_]*$/ and be at most 64 characters.");return{__beeblastEnv:!0,name:e}},{get(e,n,t){if(typeof n==="string"&&j.test(n))throw Error(`env.${n} is not supported; use env("${n}")`);return Reflect.get(e,n,t)}});function u(e,n,t,o){return{[L]:!0,kind:e,name:n,...t?{description:t}:{},config:o}}function f(e,n){let{workspaceScope:t,...o}=n;return{[G]:!0,kind:"channel",type:e,...t?{workspaceScope:t}:{},config:{...o,...t?{workspaceScope:t}:{}}}}function Fe(e){return f("telegram",e)}function Ze(e){return f("github",e)}function ze(e){return f("slack",e)}function Ve(e){return f("discord",e)}function Ye(e){return f("pancake",e)}function Xe(e){return f("zalo",e)}function Qe(e){return{[q]:!0,config:e}}function en(e){let{name:n,description:t,...o}=e;return u("agent",n,t,o)}function nn(e){return e}function tn(e){let{name:n,description:t,...o}=e;return u("workspace",n,t,o)}function on(e){let{name:n,description:t,...o}=e;return u("sandbox",n,t,o)}function rn(e){let{name:n,description:t,...o}=e;return u("skill",n,t,o)}function sn(e){let{name:n,...t}=e;return u("tool",n,void 0,t)}function an(e){let{name:n,description:t,...o}=e;return u("policy",n,t,o)}function cn(e){let{name:n,description:t,...o}=e;return u("channelRecord",n,t,o)}function ln(e){let{name:n,description:t,...o}=e;return u("cron",n,t,o)}function gn(e){return Boolean(e&&typeof e==="object"&&e[L])}function dn(e){return Boolean(e&&typeof e==="object"&&e[G])}function un(e){return Boolean(e&&typeof e==="object"&&e[q])}
|
|
5
|
-
export{b as d,
|
|
1
|
+
function b(e){if(e.events&&e.input!==void 0)throw Error("Run input accepts either `input` or `events`, not both");if(e.events&&e.events.length>0)return e.events;if(typeof e.input==="string")return[{role:"user",content:[{type:"text",text:e.input}]}];throw Error("Run input requires `input` (string) or a non-empty `events` array")}async function*x(e){let n=e.getReader(),t=new TextDecoder,o="";try{while(!0){let{done:r,value:s}=await n.read();if(r)break;o+=t.decode(s,{stream:!0});let i=o.split(`
|
|
2
|
+
`);o=i.pop()??"";for(let a of i)if(a.startsWith("data: "))yield a.slice(6)}}finally{n.releaseLock()}}import{mkdir as F,readFile as Z,writeFile as z}from"node:fs/promises";import{homedir as V}from"node:os";import{dirname as Y,join as X}from"node:path";var ve="broods",xe="_generated",m=X(V(),".broods","config.json"),E="broods.app",D="dashboard",Q="gateway";async function Ee(){let e=process.env.BROODS_TOKEN,n=process.env.BROODS_BASE_URL;if(e&&n)return{baseUrl:g(n),token:e,createdAt:new Date().toISOString()};try{let t=JSON.parse(await Z(m,"utf8"));if(typeof t.baseUrl!=="string"||!t.baseUrl)return null;return t}catch{return null}}async function De(e){await F(Y(m),{recursive:!0}),await z(m,`${JSON.stringify(e,null,2)}
|
|
3
|
+
`,"utf8")}function g(e){let n=e.length;while(n>0&&e.charCodeAt(n-1)===47)n-=1;return e.slice(0,n)}function Te(e){try{let n=new URL(e);if(!n.hostname.startsWith(`${D}.`))return;let t=n.hostname.slice(D.length+1);if(t!==E&&!t.endsWith(`.${E}`))return;return n.hostname=`${Q}.${t}`,g(n.origin)}catch{return}}import{existsSync as ee,readFileSync as N,statSync as ne}from"node:fs";import{join as te,resolve as oe}from"node:path";var v=new Map,re=new Set(Object.keys(process.env)),T=new Set;function k(e=process.cwd()){se(e);let n=ce();return{dashboardUrl:process.env.BROODS_DASHBOARD_URL??n?.dashboardUrl,baseUrl:process.env.BROODS_BASE_URL??n?.baseUrl,token:process.env.BROODS_TOKEN??n?.token,project:process.env.BROODS_PROJECT,environment:process.env.BROODS_ENVIRONMENT}}function se(e){let n=oe(e),t=process.env.BROODS_RELOAD_ENV==="1";for(let o of[".env",".env.local"]){let r=te(n,o);if(!ee(r)){v.delete(r);continue}let s=ne(r).mtimeMs,i=v.get(r);if(i&&i.mtimeMs===s)continue;v.set(r,{mtimeMs:s});for(let[a,l]of Object.entries(ie(N(r,"utf8"))))if(t||!re.has(a)||T.has(a))process.env[a]=l,T.add(a)}}function ie(e){let n={};for(let t of e.split(/\r?\n/)){let o=t.trim();if(!o||o.startsWith("#"))continue;let r=o.startsWith("export ")?o.slice(7).trim():o,s=r.indexOf("=");if(s<=0)continue;let i=r.slice(0,s).trim();if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(i))continue;n[i]=ae(r.slice(s+1).trim())}return n}function ae(e){if(e.startsWith('"')&&e.endsWith('"'))return e.slice(1,-1).replace(/\\n/g,`
|
|
4
|
+
`).replace(/\\"/g,'"');if(e.startsWith("'")&&e.endsWith("'"))return e.slice(1,-1);let n=e.indexOf(" #");return n>=0?e.slice(0,n).trimEnd():e}function ce(){try{let e=JSON.parse(N(m,"utf8"));if(typeof e.baseUrl!=="string"||typeof e.token!=="string")return null;return{baseUrl:g(e.baseUrl),...typeof e.dashboardUrl==="string"?{dashboardUrl:g(e.dashboardUrl)}:{},token:e.token}}catch{return null}}var A="https://gateway.broods.app";class U extends Error{accepted;constructor(e){super(`Ingress ${e.eventId} was accepted asynchronously with status ${e.status??"queued"}`);this.accepted=e;this.name="IngressAcceptedError"}}class le{baseUrl;apiKey;fetchImpl;constructor(e={}){k(),this.baseUrl=w(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||A),this.apiKey=e.apiKey||process.env.BROODS_API_KEY||void 0,this.fetchImpl=e.fetch??fetch}channelWebhookUrl(e){return`${this.baseUrl}${e.webhookPath.startsWith("/")?"":"/"}${e.webhookPath}`}accountWebhookUrl(e,n){let t=[e,n].map(encodeURIComponent);return`${this.baseUrl}/webhooks/${t.join("/")}`}agent(e,n){if(typeof e==="string"){let o=e,r=n??"";if(!r)throw Error(`Agent ${o} is missing a generated id. Run broods dev --once or broods deploy to sync it first.`);return{id:r,run:(s)=>this.run({...s,agentName:o,agentId:r}),runAsync:(s)=>this.runAsync({...s,agentName:o,agentId:r}),stream:(s)=>this.stream({...s,agentName:o,agentId:r})}}let t=e;if(!t.id)throw Error(`Agent ${t.name} is missing a generated id. Run broods dev --once or broods deploy to sync it first.`);return{id:t.id,run:(o)=>this.run(t,o),runAsync:(o)=>this.runAsync(t,o),stream:(o)=>this.stream(t,o)}}async run(e,n){let t=[],o="",r=n?this.stream(e,n):this.stream(e);for await(let s of r)if(t.push(s),s.type==="text-delta")o+=s.text;return{text:o,events:t}}async*stream(e,n){let t=n?{...n,agentId:e.id,agentName:e.name}:e,o=P(t,"cli"),r=n?this.scopedUrl(e):this.baseUrl,s=await this.openStream(o,r);if(!s.ok)throw Error(`Run failed: ${s.status} ${await s.text()}`);if(s.status===202)throw new U(O(await s.json(),o));if(!s.body)throw Error("Run response has no body");for await(let i of x(s.body)){let a;try{a=JSON.parse(i)}catch{continue}if(a.type==="error")throw Error(`Agent run failed: ${ye(a.error)}`);yield a}}async runAsync(e,n){let t=n?{...n,agentId:e.id,agentName:e.name}:e,o=P(t,"async"),r=n?this.scopedUrl(e,"/async"):`${this.baseUrl}/async`,s=await this.fetchJson(r,{method:"POST",headers:this.apiKeyHeaders(),body:JSON.stringify(o)});if(s.status!==202)throw Error(`Async run failed: ${s.status} ${await ue(s,"202 JSON")}`);let i=O(await s.json(),o);return{...i,conversationKey:o.conversationKey,poll:()=>this.getAsyncStatus(i),wait:(a)=>this.waitForAsyncStatus(i,a)}}async getAsyncStatus(e,n={}){let t=this.resolveStatusUrl(e,n),o=await this.fetchJson(t,{method:"GET",headers:this.apiKeyHeaders()});if(o.status===404)return{status:"not_found"};if(!o.ok)throw Error(`Status check failed: ${o.status} ${await o.text()}`);return await o.json()}async waitForAsyncStatus(e,n={}){let t=Date.now()+(n.timeoutMs??180000),o=n.intervalMs??2000;while(Date.now()<t){if(n.signal?.aborted)throw Error("Async status polling aborted.");let r=await this.getAsyncStatus(e,n);if(r.status==="awaiting_approval"||r.status==="completed"||r.status==="failed"||r.status==="expired"||r.status==="not_found")return r;await fe(o,n.signal)}throw Error("Polling timeout")}async createCron(e){let n=await this.fetchJson(`${this.baseUrl}/v1/crons`,{method:"POST",headers:this.apiKeyHeaders(),body:JSON.stringify(pe(e))});if(n.status!==201)throw Error(`Create cron job failed: ${n.status} ${await p(n)}`);return await n.json()}async listCrons(){let e=await this.fetchJson(`${this.baseUrl}/v1/crons`,{method:"GET",headers:this.apiKeyHeaders()});if(!e.ok)throw Error(`List cron jobs failed: ${e.status} ${await p(e)}`);return(await e.json()).crons}async getCron(e){let n=await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(e)}`,{method:"GET",headers:this.apiKeyHeaders()});if(n.status===404)return null;if(!n.ok)throw Error(`Get cron job failed: ${n.status} ${await p(n)}`);return await n.json()}async listCronRuns(e,n={}){let t=new URLSearchParams;if(n.limit!==void 0)t.set("limit",String(n.limit));let o=t.size>0?`?${t}`:"",r=await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(e)}/runs${o}`,{method:"GET",headers:this.apiKeyHeaders()});if(!r.ok)throw Error(`List cron job runs failed: ${r.status} ${await p(r)}`);return(await r.json()).runs}async updateCron(e,n){let t=await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(e)}`,{method:"PATCH",headers:this.apiKeyHeaders(),body:JSON.stringify(n)});if(!t.ok)throw Error(`Update cron job failed: ${t.status} ${await p(t)}`);return await t.json()}async deleteCron(e){let n=await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(e)}`,{method:"DELETE",headers:this.apiKeyHeaders()});if(n.status===404)return!1;if(!n.ok)throw Error(`Delete cron job failed: ${n.status} ${await p(n)}`);return(await n.json()).deleted}scopedUrl(e,n=""){if(e.projectSlug&&e.environmentSlug&&e.endpointId)return`${this.baseUrl}/v1/${encodeURIComponent(e.projectSlug)}/agents/${encodeURIComponent(e.environmentSlug)}/${encodeURIComponent(e.endpointId)}${n}`;return`${this.baseUrl}${n}`}async openStream(e,n){if(this.apiKey)return await this.fetchCore(e,n,{Authorization:`Bearer ${this.apiKey}`});throw Error(`BroodsClient streams directly from the core service at ${this.baseUrl}. Provide apiKey. For a self-hosted core service, set host/baseUrl or BROODS_HOST/BROODS_BASE_URL.`)}async fetchCore(e,n,t){try{return await this.fetchImpl(n,{method:"POST",headers:{"Content-Type":"application/json",Accept:"text/event-stream",...t},body:JSON.stringify(e)})}catch(o){throw Error(`Cannot access the broods core service at ${n}. The SDK uses ${A} by default; set host/baseUrl or BROODS_HOST/BROODS_BASE_URL to your own core service URL if your account uses a custom deployment. Cause: ${o instanceof Error?o.message:String(o)}`)}}apiKeyHeaders(){if(!this.apiKey)throw Error("BroodsClient requires apiKey or BROODS_API_KEY.");return{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`}}async fetchJson(e,n){try{return await this.fetchImpl(e,{...n,headers:{"Content-Type":"application/json",...n.headers??{}}})}catch(t){throw Error(`Cannot access the broods core service at ${e}. Cause: ${t instanceof Error?t.message:String(t)}`)}}resolveStatusUrl(e,n){if(typeof e!=="string")return e.statusUrl;if(/^https?:\/\//.test(e))return e;if(!n.agentId)throw Error("Polling by status id requires agentId.");return de(this.baseUrl,e,n.agentId)}}function w(e){let n=e.trim(),t=/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(n)?n:`https://${n}`;return g(t)}function P(e,n){let t=e.eventId??`${n}-${Date.now()}`;return{agentId:e.agentId,eventId:t,conversationKey:e.conversationKey??t,events:b(e),...e.mode!==void 0?{mode:e.mode}:{},...e.idempotencyKey!==void 0?{idempotencyKey:e.idempotencyKey}:{},...e.system!==void 0?{system:e.system}:{},...e.model!==void 0?{model:e.model}:{}}}function O(e,n){if(!e||typeof e!=="object")throw Error("Async response must be an object");let t=e.statusUrl;if(typeof t!=="string"||t.length===0)throw Error("Async response missing statusUrl");let o=ge(t);if(!o.statusId)throw Error("Async response statusUrl missing status id");let r=typeof e.eventId==="string"?e.eventId:o.statusId,s=e.status,i=e.requestedMode;return{statusUrl:t,statusId:o.statusId,eventId:r,agentId:o.agentId??n.agentId,...s==="accepted"||s==="queued"||s==="applied"||s==="processing"?{status:s}:{},...i==="reject"||i==="followup"||i==="collect"||i==="steer"?{requestedMode:i}:{}}}function ge(e){let n=new URL(e),t=n.pathname.match(/\/status\/([^/]+)$/);return{statusId:t?.[1]?decodeURIComponent(t[1]):void 0,agentId:n.searchParams.get("agentId")??void 0}}function de(e,n,t){return`${w(e)}/status/${encodeURIComponent(n)}?agentId=${encodeURIComponent(t)}`}async function ue(e,n){if((e.headers.get("content-type")??"").toLowerCase().includes("text/event-stream"))return await e.body?.cancel().catch(()=>{}),`expected ${n}, but the server returned an SSE stream. This usually means the core deployment routed /async to the direct streaming runner instead of the async handler.`;let o=await e.text();if(o.length<=2000)return o;return`${o.slice(0,2000)}... [truncated ${o.length-2000} chars]`}async function p(e){let n=await e.text();if(n.includes("Request body must include eventId and conversationKey"))return`${n}. Cron job APIs must be served by the configured baseUrl. Prefer defining stable cron jobs with defineCron(...) in broods/ and syncing with \`broods dev\` or \`broods deploy\`.`;return n}function pe(e){if("agentId"in e)return e;let n=e.agent,t=typeof n==="string"?n:n.id,{agent:o,...r}=e;return{...r,agentId:t}}function fe(e,n){return new Promise((t,o)=>{if(n?.aborted){o(Error("Async status polling aborted."));return}let r=setTimeout(t,e);n?.addEventListener("abort",()=>{clearTimeout(r),o(Error("Async status polling aborted."))},{once:!0})})}function ye(e){if(typeof e==="string")return e;if(!e||typeof e!=="object")return String(e);let n=e,t=n.data?.error?.message??n.message??n.responseBody??JSON.stringify(e),o=n.name?`${n.name}: `:"",r=n.statusCode?` (HTTP ${n.statusCode})`:"";return`${o}${t}${r}`}var me=2000,W=0,h=1;class he{baseUrl;apiKey;WebSocketImpl;connectTimeoutMs;constructor(e={}){if(k(),this.baseUrl=B(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||A),this.apiKey=e.apiKey||process.env.BROODS_API_KEY||"",!this.apiKey)throw Error("BroodsWebSocketClient requires apiKey or BROODS_API_KEY.");this.WebSocketImpl=e.WebSocket,this.connectTimeoutMs=e.connectTimeoutMs??me}subscribe(e,n={}){let t=this.resolveWebSocket(),o=this.buildUrl(e),r=Se(e),s=M(this.baseUrl),i=new t(o),a=!1,l=!1,d=setTimeout(()=>{if(!a)y(s)},this.connectTimeoutMs),I=(c=1000,R="client closed")=>{if(l)return;if(l=!0,clearTimeout(d),e.signal?.removeEventListener("abort",S),i.readyState===h||i.readyState===W)i.close(c,R)},y=(c)=>{n.onError?.(c),I(1011,"websocket error")},J=()=>{n.onDone?.(),I(1000,"done")},S=()=>{if(i.readyState===h)i.send(JSON.stringify({type:"cancel"}));y(Error("WebSocket run aborted."))};return e.signal?.addEventListener("abort",S,{once:!0}),i.onopen=()=>{if(a=!0,clearTimeout(d),e.signal?.aborted){S();return}i.send(JSON.stringify({type:"execute",agentId:r,events:b(e),sessionId:e.sessionId,...e.eventId!==void 0?{eventId:e.eventId}:{},...e.mode!==void 0?{mode:e.mode}:{},...e.idempotencyKey!==void 0?{idempotencyKey:e.idempotencyKey}:{},...e.system!==void 0?{system:e.system}:{},...e.model!==void 0?{model:e.model}:{}}))},i.onmessage=(c)=>{if(typeof c.data!=="string")return;let R=K(c.data);if(!R)return;let C=_(R,n);switch(n.onMessage?.(C),C.type){case"meta":n.onMeta?.(C);break;case"done":J();break;case"error":y(Error(H(C.error)));break}},i.onerror=()=>{y(s)},i.onclose=(c)=>{if(clearTimeout(d),e.signal?.removeEventListener("abort",S),l||c.code===1000)return;y(Error(c.reason||"WebSocket connection closed."))},{url:o,sendControl:function(c){if(i.readyState!==h)throw Error("WebSocket is not open");i.send(JSON.stringify({type:"control",...c}))},close:I}}attach(e,n={}){let t=this.resolveWebSocket(),o=this.buildUrl(e),r=new t(o),s=!1,i=(a=1000,l="client closed")=>{if(s)return;if(s=!0,r.readyState===h||r.readyState===W)r.close(a,l)};return r.onopen=()=>r.send(JSON.stringify({type:"attach",...e,signal:void 0,agent:void 0})),r.onmessage=(a)=>{if(typeof a.data!=="string")return;let l=K(a.data);if(!l)return;let d=_(l,n);if(n.onMessage?.(d),d.type==="done")n.onDone?.();if(d.type==="error")n.onError?.(Error(H(d.error)))},r.onerror=()=>n.onError?.(M(this.baseUrl)),r.onclose=(a)=>{if(!s&&a.code!==1000)n.onError?.(Error(a.reason||"WebSocket connection closed."))},{url:o,sendControl:function(a){if(r.readyState!==h)throw Error("WebSocket is not open");r.send(JSON.stringify({type:"control",...a}))},close:i}}async*stream(e){let n=[],t=!1,o=null,r=null,s=()=>{r?.(),r=null},i=this.subscribe(e,{onMessage:function(a){n.push(a),s()},onDone:function(){t=!0,s()},onError:function(a){o=a,s()}});try{while(!0){if(n.length>0){yield n.shift();continue}if(o)throw o;if(t)return;await new Promise((a)=>{r=a})}}finally{i.close()}}buildUrl(e){let n=Re(e),t=e.projectSlug??e.agent?.projectSlug,o=e.environmentSlug??e.agent?.environmentSlug,r=t?`/${t}`:"",s=o?`/${o}`:"";return`${Ce(this.baseUrl)}/v1${r}/agents${s}/${encodeURIComponent(n)}/ws?token=${encodeURIComponent(this.apiKey)}`}resolveWebSocket(){let e=this.WebSocketImpl??globalThis.WebSocket;if(!e)throw Error("WebSocket is not available. Pass a WebSocket implementation in BroodsWebSocketClient options.");return e}}function Se(e){let n=e.agentId??e.agent?.id??e.endpointId;if(!n)throw Error("WebSocket run requires agent, agentId, or endpointId.");return n}function Re(e){let n=e.endpointId??e.agent?.endpointId??e.agent?.id;if(!n)throw Error("WebSocket run requires agent or endpointId.");return n}function Ce(e){let n=new URL(B(e));if(n.protocol==="https:")n.protocol="wss:";if(n.protocol==="http:")n.protocol="ws:";return g(n.toString())}function B(e){let n=e.trim();if(/^wss?:\/\//.test(n))return g(n);return w(n)}function M(e){return Error(`Cannot access the WebSocket service at ${e}.`)}function _(e,n){if(e.type!=="output")return e;let t=e;return n.onOutput?.(t),t.data}function K(e){try{let n=JSON.parse(e);return typeof n==="object"&&n!==null&&typeof n.type==="string"?n:null}catch{return null}}function H(e){if(typeof e==="string")return e;if(e&&typeof e==="object"&&typeof e.message==="string")return e.message;return e===void 0?"WebSocket stream error.":JSON.stringify(e)}var L=Symbol.for("broods.resource"),q=Symbol.for("broods.config"),G=Symbol.for("broods.channel"),j=/^[A-Z][A-Z0-9_]{0,63}$/;var Je=new Proxy((e)=>{if(!j.test(e))throw Error("env name must match /^[A-Z][A-Z0-9_]*$/ and be at most 64 characters.");return{__beeblastEnv:!0,name:e}},{get:function(e,n,t){if(typeof n==="string"&&j.test(n))throw Error(`env.${n} is not supported; use env("${n}")`);return Reflect.get(e,n,t)}});function u(e,n,t,o){return{[L]:!0,kind:e,name:n,...t?{description:t}:{},config:o}}function f(e,n){let{workspaceScope:t,...o}=n;return{[G]:!0,kind:"channel",type:e,...t?{workspaceScope:t}:{},config:{...o,...t?{workspaceScope:t}:{}}}}function Fe(e){return f("telegram",e)}function Ze(e){return f("github",e)}function ze(e){return f("slack",e)}function Ve(e){return f("discord",e)}function Ye(e){return f("pancake",e)}function Xe(e){return f("zalo",e)}function Qe(e){return{[q]:!0,config:e}}function en(e){let{name:n,description:t,...o}=e;return u("agent",n,t,o)}function nn(e){return e}function tn(e){let{name:n,description:t,...o}=e;return u("workspace",n,t,o)}function on(e){let{name:n,description:t,...o}=e;return u("sandbox",n,t,o)}function rn(e){let{name:n,description:t,...o}=e;return u("skill",n,t,o)}function sn(e){let{name:n,...t}=e;return u("tool",n,void 0,t)}function an(e){let{name:n,description:t,...o}=e;return u("policy",n,t,o)}function cn(e){let{name:n,description:t,...o}=e;return u("channelRecord",n,t,o)}function ln(e){let{name:n,description:t,...o}=e;return u("cron",n,t,o)}function gn(e){return Boolean(e&&typeof e==="object"&&e[L])}function dn(e){return Boolean(e&&typeof e==="object"&&e[G])}function un(e){return Boolean(e&&typeof e==="object"&&e[q])}
|
|
5
|
+
export{b as d,x as e,ve as f,xe as g,m as h,Ee as i,De as j,g as k,Te as l,k as m,A as n,U as o,le as p,w as q,he as r,Ce as s,Je as t,Fe as u,Ze as v,ze as w,Ve as x,Ye as y,Xe as z,Qe as A,en as B,nn as C,tn as D,on as E,rn as F,sn as G,an as H,cn as I,ln as J,gn as K,dn as L,un as M};
|