broods 0.36.1 → 0.37.0

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 CHANGED
@@ -101,6 +101,30 @@ declare const MODEL_PROVIDERS: {
101
101
  };
102
102
  type AccountModelProviderName = keyof typeof MODEL_PROVIDERS;
103
103
 
104
+ /**
105
+ * Agent config validation, the only copy of it: Convex config HTTP checks a
106
+ * config on write and core re-checks the stored config on every run. Pure
107
+ * module: safe for the default Convex runtime. The public projection lives in
108
+ * ./responses.ts.
109
+ */
110
+
111
+ declare const AGENT_HARNESS_TYPES: readonly ["claude-code", "codex", "deepagents", "opencode", "pi"];
112
+ declare const AGENT_HARNESS_DEBUG_LEVELS: readonly ["error", "warn", "info", "debug", "trace"];
113
+ declare const AGENT_HARNESS_PERMISSION_MODES: readonly ["allow-reads", "allow-edits", "allow-all"];
114
+ declare const AGENT_LIFECYCLE_EVENT_NAMES: readonly ["agent.started", "agent.step.finished", "agent.finished", "agent.failed", "agent.approval.required", "tool.call.started", "tool.call.finished", "tool.result", "subagent.task.started", "subagent.task.finished"];
115
+
116
+ /**
117
+ * Shared validation for MCP server registrations (#331). One normalizer
118
+ * serves every write path (CLI sync, direct API, dashboard). A `url` makes an "http" row
119
+ * core connects to over the stateless 2026-07-28 transport; a `bundle` makes
120
+ * a "hosted" row served by the tool-runner Lambda, hashed here so sha256
121
+ * always travels with the bundle. Auth header values may carry ${NAME}
122
+ * account env refs; they resolve into the encrypted agent config at sync
123
+ * time, never on this row, and credential-bearing headers must use one
124
+ * instead of an inline secret. `oauth` follows the same rule: clientSecret
125
+ * and refreshToken must be ${NAME} refs, so the row never holds a secret.
126
+ */
127
+
104
128
  /**
105
129
  * OAuth 2.0 refresh-token grant for an external row. Core mints access tokens
106
130
  * at connect time and sends `Authorization: Bearer <token>`, so a server
@@ -116,20 +140,18 @@ interface McpOauth {
116
140
  }
117
141
 
118
142
  /**
119
- * Agent configuration: types for the per-agent settings object, input
120
- * normalization, encryption helpers, patch-merge, and redaction.
143
+ * Agent configuration: types for the per-agent settings object, the runtime
144
+ * projection of a stored config, and encryption helpers. The validation rules
145
+ * are the config plane's (`@broods/convex/model/agentRules`), so a config is
146
+ * judged the same on write and on every run.
121
147
  * Account types and auth live in `./accounts.ts` and `../auth.ts`.
122
148
  */
123
149
 
124
- declare const AGENT_HARNESS_TYPES: readonly ["claude-code", "codex", "deepagents", "opencode", "pi"];
125
- declare const AGENT_HARNESS_DEBUG_LEVELS: readonly ["error", "warn", "info", "debug", "trace"];
126
- declare const AGENT_HARNESS_PERMISSION_MODES: readonly ["allow-reads", "allow-edits", "allow-all"];
127
150
  interface AgentConfig {
128
151
  agent?: AgentBehaviorConfig;
129
152
  harness?: AgentHarnessConfig;
130
153
  model?: AgentModelConfig;
131
154
  provider?: AgentProviderConfig;
132
- sandbox?: string;
133
155
  sandboxes?: string[];
134
156
  workspaces?: AgentWorkspaceRef[];
135
157
  session?: AgentSessionConfig;
@@ -297,7 +319,7 @@ interface AgentWebhookHookConfig {
297
319
  events?: AgentLifecycleEventName[];
298
320
  [key: string]: unknown;
299
321
  }
300
- type AgentLifecycleEventName = "agent.started" | "agent.step.finished" | "agent.finished" | "agent.failed" | "agent.approval.required" | "tool.call.started" | "tool.call.finished" | "tool.result" | "subagent.task.started" | "subagent.task.finished";
322
+ type AgentLifecycleEventName = (typeof AGENT_LIFECYCLE_EVENT_NAMES)[number];
301
323
 
302
324
  type AgentToolsConfig = Record<string, AgentToolConfig>;
303
325
  interface AgentToolConfig {
@@ -0,0 +1,4 @@
1
+ import{I}from"./chunk-pxzdwb6g.js";import{ce,le,e,o,i,l,r,t,u,S,s,p,n}from"./cli/index.js";import{spawn as V}from"node:child_process";import{hostname as ee}from"node:os";import{performance as T}from"node:perf_hooks";var j=["cursor_position","double_click","hold_key","key","left_click","left_click_drag","left_mouse_down","left_mouse_up","middle_click","mouse_move","right_click","screenshot","scroll","triple_click","type","wait","zoom"];var k={badFrame:{code:4400,reason:"Malformed frame"},occupied:{code:4423,reason:"Already connected from another daemon"},replaced:{code:4409,reason:"Replaced by a newer connection"},unauthorized:{code:4401,reason:"Unauthorized; check BROODS_API_KEY"},unknownSandbox:{code:4404,reason:"No machine sandbox with that name in this account"}},N="/v1/machines/ws",H=t({action:p(j),coordinate:r(o().int()).length(2).optional().describe("[x, y] in screenshot pixels for mouse actions."),start_coordinate:r(o().int()).length(2).optional().describe("[x, y] where a left_click_drag starts."),text:e().optional().describe("Text for type, the key chord for key and hold_key, or modifiers to hold during a click, drag or scroll."),scroll_direction:p(["down","left","right","up"]).optional(),scroll_amount:o().int().min(1).optional().describe("Scroll clicks, default 3."),duration:o().min(0).max(300).optional().describe("Seconds for wait and hold_key."),region:r(o().int()).length(4).optional().describe("[x0, y0, x1, y1] of the screenshot to zoom into."),repeat:o().int().min(1).max(100).optional().describe("How many times to press the key.")}),P=H.extend({type:n("computer"),id:e()}),U=t({type:n("computer-result"),id:e(),text:e().optional(),image:t({data:e(),mediaType:e()}).optional(),app:e().optional(),error:e().optional()}),B=t({type:n("exec"),id:e(),code:e(),cwd:e().optional(),env:s(e(),e()).optional(),timeoutSeconds:o().positive(),outputLimitBytes:o().positive()}),K=t({type:n("hello"),sandbox:e().min(1),hostname:e().optional(),platform:e().optional(),computer:i().optional(),mcp:r(e()).optional(),instance:e().optional(),force:i().optional()}),W=t({type:n("mcp-call"),id:e(),server:e(),tool:e(),args:s(e(),l())}),G=t({type:n("mcp-list"),id:e(),server:e()}),J=t({type:n("mcp-result"),id:e(),result:u({content:r(l())}).optional(),error:e().optional()}),X=t({type:n("mcp-tools"),id:e(),tools:r(u({name:e(),inputSchema:s(e(),l())})).optional(),error:e().optional()}),Y=t({type:n("ready"),sandboxId:e()}),q=t({type:n("result"),id:e(),exitCode:o().nullable(),stdout:e(),stderr:e(),durationMs:o(),timedOut:i(),truncated:i()}),Z=S("type",[P,B,W,G,Y]),me=S("type",[U,K,J,X,q]);function O(c){let a=new URL(N,c);return a.protocol=a.protocol==="https:"?"wss:":"ws:",a.toString()}function D(c){return Q(Z,c)}function Q(c,a){if(typeof a!=="string")return null;try{let m=c.safeParse(JSON.parse(a));return m.success?m.data:null}catch{return null}}var te=new Set([k.occupied.code,k.replaced.code,k.unauthorized.code,k.unknownSandbox.code]),R=72,L=30000,z=1000;function oe(c,a,m){return new Promise((y)=>{let d=T.now(),b=new C(c.outputLimitBytes),h=new C(c.outputLimitBytes),M=!1,_=!1,w=(v,E)=>{if(_)return;if(_=!0,clearTimeout(g),m?.removeEventListener("abort",f),E)h.append(Buffer.from(`${E}
2
+ `));y({type:"result",id:c.id,exitCode:v,stdout:b.text(),stderr:h.text(),durationMs:Math.round(T.now()-d),timedOut:M,truncated:b.truncated||h.truncated})},x=V("bash",["-lc",c.code],{cwd:c.cwd??a,env:{...process.env,...c.env},detached:!0,stdio:["ignore","pipe","pipe"]}),F=()=>{if(!x.pid)return;try{process.kill(-x.pid,"SIGKILL")}catch{x.kill("SIGKILL")}},g=setTimeout(()=>{M=!0,F()},c.timeoutSeconds*1000),f=()=>{F(),w(null,"stopped: the daemon closed its socket")};if(m?.addEventListener("abort",f,{once:!0}),x.stdout.on("data",(v)=>b.append(v)),x.stderr.on("data",(v)=>h.append(v)),x.on("error",(v)=>w(null,v.message)),x.on("close",(v)=>w(v)),m?.aborted)f()})}async function Fe(c){let a=le(),m=crypto.randomUUID(),y=c.mcpFile?await ie(c.mcpFile,c.log):null,d=c.computer?await ne(c.log):null,b=z;try{while(!c.signal.aborted){let h=Date.now(),M=await ae(c,m,a,d,y);if(c.signal.aborted)return;if(te.has(M.code))throw Error(M.reason||`core closed the socket (${M.code})`);if(Date.now()-h>L)b=z;c.log(`disconnected (${M.reason||M.code}), reconnecting in ${Math.round(b/1000)}s`),await ce(b,c.signal),b=Math.min(b*2,L)}}finally{d?.stop(),await y?.stop()}}class C{#o=[];#e;#t=0;truncated=!1;constructor(c){this.#e=c}append(c){if(this.#t>=this.#e){this.truncated=!0;return}let a=this.#e-this.#t;if(c.byteLength>a){this.#o.push(c.subarray(0,a)),this.#t=this.#e,this.truncated=!0;return}this.#o.push(c),this.#t+=c.byteLength}text(){let c=Buffer.concat(this.#o).toString("utf8");return this.truncated?`${c}
3
+ [output truncated]`:c}}function re(c){let a=[c.action];if(c.coordinate)a.push(`at ${c.coordinate.join(",")}`);if(c.text!==void 0)a.push(JSON.stringify(A(c.text)));if(c.region)a.push(`region ${c.region.join(",")}`);return a.join(" ")}function A(c){let a=c.trim().split(`
4
+ `)[0]??"";return a.length>R?`${a.slice(0,R)}…`:a}async function ne(c){let{startDesktop:a}=await import("./chunk-5tnnqapc.js"),{display:m,driver:y,permissions:d}=await a(!1);if(c(`computer use on, ${m.width}x${m.height} screenshots`),!d.screenRecording||!d.accessibility)c(" screen recording or accessibility is missing, run `broods machine --doctor --request`");return y}async function ie(c,a){let{McpHost:m,readMcpServersFile:y}=await import("./chunk-794tw7dm.js"),d=new m(y(c),a);return a(`mcp servers from ${c}: ${d.names().join(", ")}`),d}async function se(c,a,m){try{if(c.type==="mcp-list"){let d=await a.listTools(c.server);return m(` mcp ${c.server}: ${d.length} tools`),{type:"mcp-tools",id:c.id,tools:d}}m(` mcp ${c.server}.${c.tool}`);let y=await a.callTool(c.server,c.tool,c.args);return{type:"mcp-result",id:c.id,result:y}}catch(y){let d=y instanceof Error?y.message:String(y);return m(` mcp ${c.server}: ${d}`),{type:c.type==="mcp-call"?"mcp-result":"mcp-tools",id:c.id,error:d}}}function ae(c,a,m,y,d){return new Promise((b)=>{let h=new m(O(c.baseUrl),I(c.apiKey)),M=new AbortController,_=()=>h.close(1000,"daemon stopped"),w=(F)=>{if(h.readyState===h.OPEN)h.send(JSON.stringify(F))},x=(F,g)=>{c.signal.removeEventListener("abort",_),M.abort(),b({code:F,reason:g})};c.signal.addEventListener("abort",_,{once:!0}),h.onopen=()=>w({type:"hello",sandbox:c.sandbox,hostname:ee(),platform:process.platform,computer:y!==null,mcp:d?.names(),instance:a,force:c.force}),h.onmessage=(F)=>{let g=D(F.data);if(g?.type==="ready"){c.log(`connected as ${c.sandbox} (${g.sandboxId})`);return}if(g?.type==="computer"&&y){c.log(` ${re(g)}`),y.run(g).then((f)=>{if(f.error)c.log(` error: ${f.error}`);w(f)});return}if((g?.type==="mcp-call"||g?.type==="mcp-list")&&d){se(g,d,c.log).then(w);return}if(g?.type!=="exec")return;c.log(`$ ${A(g.code)}`),oe(g,c.cwd,M.signal).then((f)=>{c.log(` exit ${f.exitCode??"none"} in ${f.durationMs}ms${f.timedOut?" (timed out)":""}`),w(f)})},h.onerror=()=>x(1006,"connection failed"),h.onclose=(F)=>x(F.code,F.reason)})}export{Fe as runMachineDaemon};
@@ -1 +1 @@
1
- import"./chunk-s6j5t96c.js";import{Pe,e,r,t,s}from"./cli/index.js";import{readFileSync as d}from"node:fs";import{dirname as m,resolve as p}from"node:path";import{Client as g}from"@modelcontextprotocol/client";import{StdioClientTransport as h}from"@modelcontextprotocol/client/stdio";var f={name:"broods-machine",version:"1.0.0"},u=t({command:e().min(1),args:r(e()).default([]),env:s(e(),e()).optional(),cwd:e().optional()}),w=t({mcpServers:s(e(),u).refine((n)=>Object.keys(n).length>0,"lists no MCP servers")});class y{#t=new Map;#e;#r;constructor(n,i){this.#r=n,this.#e=i}async callTool(n,i,o){return await(await this.#n(n)).callTool({name:i,arguments:o})}async listTools(n){return(await(await this.#n(n)).listTools()).tools}names(){return[...this.#r.keys()]}async stop(){let n=[...this.#t.values()];this.#t.clear(),await Promise.all(n.map((i)=>i.then((o)=>o.close()).catch(()=>{})))}#n(n){let i=this.#r.get(n);if(!i)return Promise.reject(Error(`no MCP server named "${n}" in the --mcp file`));let o=this.#t.get(n);if(o)return o;let c=(async()=>{this.#e(`mcp ${n}: ${[i.command,...i.args].join(" ")}`);let a=new h({...i,env:{...v(),...i.env},stderr:"inherit"});a.onclose=()=>{if(this.#e(`mcp ${n}: exited`),this.#t.get(n)===c)this.#t.delete(n)};let l=new g(f);return await l.connect(a),l})();return c.catch(()=>{if(this.#t.get(n)===c)this.#t.delete(n)}),this.#t.set(n,c),c}}function R(n){let i=p(n),o=w.safeParse(S(i));if(!o.success)throw Error(`${i}: ${Pe(o.error)}`);return new Map(Object.entries(o.data.mcpServers).map(([c,a])=>[c,a.cwd?{...a,cwd:p(m(i),a.cwd)}:a]))}function v(){let n={};for(let[i,o]of Object.entries(process.env))if(typeof o==="string")n[i]=o;return n}function S(n){try{return JSON.parse(d(n,"utf8"))}catch(i){throw Error(`could not read ${n}: ${i instanceof Error?i.message:String(i)}`)}}export{y as McpHost,R as readMcpServersFile};
1
+ import"./chunk-txsgyj2d.js";import{Pe,e,r,t,s}from"./cli/index.js";import{readFileSync as d}from"node:fs";import{dirname as m,resolve as p}from"node:path";import{Client as g}from"@modelcontextprotocol/client";import{StdioClientTransport as h}from"@modelcontextprotocol/client/stdio";var f={name:"broods-machine",version:"1.0.0"},u=t({command:e().min(1),args:r(e()).default([]),env:s(e(),e()).optional(),cwd:e().optional()}),w=t({mcpServers:s(e(),u).refine((n)=>Object.keys(n).length>0,"lists no MCP servers")});class y{#t=new Map;#e;#r;constructor(n,i){this.#r=n,this.#e=i}async callTool(n,i,o){return await(await this.#n(n)).callTool({name:i,arguments:o})}async listTools(n){return(await(await this.#n(n)).listTools()).tools}names(){return[...this.#r.keys()]}async stop(){let n=[...this.#t.values()];this.#t.clear(),await Promise.all(n.map((i)=>i.then((o)=>o.close()).catch(()=>{})))}#n(n){let i=this.#r.get(n);if(!i)return Promise.reject(Error(`no MCP server named "${n}" in the --mcp file`));let o=this.#t.get(n);if(o)return o;let c=(async()=>{this.#e(`mcp ${n}: ${[i.command,...i.args].join(" ")}`);let a=new h({...i,env:{...v(),...i.env},stderr:"inherit"});a.onclose=()=>{if(this.#e(`mcp ${n}: exited`),this.#t.get(n)===c)this.#t.delete(n)};let l=new g(f);return await l.connect(a),l})();return c.catch(()=>{if(this.#t.get(n)===c)this.#t.delete(n)}),this.#t.set(n,c),c}}function R(n){let i=p(n),o=w.safeParse(S(i));if(!o.success)throw Error(`${i}: ${Pe(o.error)}`);return new Map(Object.entries(o.data.mcpServers).map(([c,a])=>[c,a.cwd?{...a,cwd:p(m(i),a.cwd)}:a]))}function v(){let n={};for(let[i,o]of Object.entries(process.env))if(typeof o==="string")n[i]=o;return n}function S(n){try{return JSON.parse(d(n,"utf8"))}catch(i){throw Error(`could not read ${n}: ${i instanceof Error?i.message:String(i)}`)}}export{y as McpHost,R as readMcpServersFile};
@@ -1 +1 @@
1
- import{C,a,D,z,B,V,Z,b,x,H,re,ne,me,fe,he,ge,oe,se,ye,ve,be,O}from"./chunk-3ryp98a2.js";import"./chunk-s6j5t96c.js";var J=class{_readBuffer;_started=!1;_closed=!1;constructor(r=oe.stdin,o=oe.stdout,c){this._stdin=r,this._stdout=o,this._readBuffer=new he({maxBufferSize:c?.maxBufferSize})}onclose;onerror;onmessage;_ondata=(r)=>{try{this._readBuffer.append(r),this.processReadBuffer()}catch(o){this.onerror?.(o),this.close().catch(()=>{})}};_onerror=(r)=>{this.onerror?.(r)};_onstdouterror=(r)=>{this.onerror?.(r),this.close().catch(()=>{})};async start(){if(this._started)throw Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");this._started=!0,this._stdin.on("data",this._ondata),this._stdin.on("error",this._onerror),this._stdout.on("error",this._onstdouterror)}processReadBuffer(){while(!0)try{let r=this._readBuffer.readMessage();if(r===null)break;this.onmessage?.(r)}catch(r){this.onerror?.(r)}}async close(){if(this._closed)return;if(this._closed=!0,this._stdin.off("data",this._ondata),this._stdin.off("error",this._onerror),this._stdout.off("error",this._onstdouterror),this._stdin.listenerCount("data")===0)this._stdin.pause();this._readBuffer.clear(),this.onclose?.()}send(r){if(this._closed)return Promise.reject(Error("StdioServerTransport is closed"));return new Promise((o,c)=>{let l=ge(r),t=!1,f=(_)=>{if(t)return;t=!0,this._stdout.off("error",f),this._stdout.off("drain",p),c(_)},p=()=>{if(t)return;t=!0,this._stdout.off("error",f),this._stdout.off("drain",p),o()};if(this._stdout.once("error",f),this._stdout.write(l)){if(t)return;t=!0,this._stdout.off("error",f),o()}else if(!t)this._stdout.once("drain",p)})}},k=3000,L=class{onclose;onerror;onmessage;_closed=!1;_pendingRequests=new Set;_drainWaiters=[];constructor(r,o,c){this._wire=r,this._onInstanceClose=o,this._outboundIntercept=c}async start(){}async send(r,o){if(H(r)||re(r)){let{id:c}=r;if(c!==void 0)this._settle(c)}if(this._closed)return;if(this._outboundIntercept?.(r)==="handled")return;return this._wire.send(r,o)}setProtocolVersion=(r)=>{this._wire.setProtocolVersion?.(r)};deliver(r,o){if(this._closed)return;if(b(r))this._pendingRequests.add(r.id);else if(x(r)&&r.method==="notifications/cancelled"){let c=r.params?.requestId;if(c!==void 0)this._settle(c)}this.onmessage?.(r,o)}async whenRequestsAnswered(r){if(this._closed||this._pendingRequests.size===0)return!0;return await new Promise((o)=>{let c=()=>{clearTimeout(l),o(!0)},l=setTimeout(()=>{this._drainWaiters=this._drainWaiters.filter((t)=>t!==c),o(!1)},r);this._drainWaiters.push(c)})}async close(){if(this._closed)return;this._closed=!0,this._pendingRequests.clear(),this._releaseDrainWaiters();try{this._onInstanceClose()}finally{this.onclose?.()}}_settle(r){if(this._pendingRequests.delete(r),this._pendingRequests.size===0)this._releaseDrainWaiters()}_releaseDrainWaiters(){let r=this._drainWaiters;this._drainWaiters=[];for(let o of r)o()}};function U(r){let o=r.params;if(r.method==="initialize"&&!ne(o)){let f=o!==null&&typeof o==="object"&&typeof o.protocolVersion==="string"?o.protocolVersion:void 0;return{kind:"legacy",reason:"initialize",...f!==void 0&&{requestedVersion:f}}}if(!B(o))return{kind:"legacy",reason:"no-claim"};let c=z(o),l=(c===void 0?[]:Z(c))[0];if(l!==void 0)return{kind:"invalid-envelope",issue:l};let t=V(o);if(t===void 0||!C.includes(t))return{kind:"unsupported-revision",requested:t??"unknown"};return{kind:"modern",revision:t,classification:{era:"modern",revision:t}}}function Q(r,o={}){let c=o.legacy??"serve",l=o.transport??new J,t={phase:"opening"},f,p=!1,_=()=>p||t.phase==="closed",d=(e)=>{try{o.onerror?.(e)}catch{}},y=(e,n,i,s)=>l.send({jsonrpc:"2.0",id:e,error:{code:n,message:i,...s!==void 0&&{data:s}}}).catch((u)=>d(h(u))),w=new ye(o.maxSubscriptions??se),M=(e)=>{if(!x(e))return;let n=w.routeOutbound(e);if(n==="passthrough")return;for(let i of n)l.send({jsonrpc:"2.0",...i}).catch((s)=>d(h(s)));return"handled"},I=async(e)=>{if(b(e)&&e.method==="subscriptions/listen"){let n=z(e.params),i=B(e.params)?(n===void 0?[]:Z(n))[0]:{key:"_meta",problem:"the per-request envelope is required on protocol revision 2026-07-28"},s=V(e.params),u;if(i!==void 0)u={jsonrpc:"2.0",id:e.id,error:{code:-32602,message:`Invalid _meta envelope: ${i.key}: ${i.problem}`}};else if(s===void 0||!C.includes(s)){let v=new D({supported:[...C],requested:s??"unknown"});u={jsonrpc:"2.0",id:e.id,error:{code:v.code,message:v.message,data:v.data}}}else u=w.serve(e);return await l.send("error"in u?u:{jsonrpc:"2.0",method:u.method,params:u.params}).catch((v)=>d(h(v))),!0}if(x(e)&&e.method==="notifications/cancelled"){let n=e.params?.requestId;if(n!==void 0&&w.cancel(n))return!0}return!1},q=(e,n,i)=>{let s=me({kind:"legacy",reason:n,...i!==void 0&&{requestedVersion:i}},C);if(s===void 0)return Promise.resolve();return d(Error(`Rejected 2025-era request on a modern-only stdio connection (${s.cell}): ${s.message}`)),y(e.id,s.code,s.message,s.data)},T=(e)=>{if(p||e===f)return;P()},m=async(e,n)=>{let i=await r({era:e}),s=i instanceof O?i.server:i;if(e==="modern")fe(s,n),ve(s,C),w.setServerCapabilities(s.getCapabilities(),be(s));let u=new L(l,()=>T(u),e==="modern"?M:void 0);return await i.connect(u),{product:i,channel:u}},R=(e)=>e.product.close().catch((n)=>d(h(n))),N=async(e)=>{f=e.channel;try{if(!await e.channel.whenRequestsAnswered(k))d(Error(`Discarded the probe instance with requests still unanswered after ${k}ms; continuing with the fallback`));await e.product.close()}catch(n){d(h(n))}finally{f=void 0}},W=async(e)=>{if(t.phase==="closed")return;if(t.phase==="pinned"){if(t.era==="modern"&&b(e)&&e.method==="initialize"&&!ne(e.params)){await q(e,"initialize",e.params!==null&&typeof e.params==="object"&&typeof e.params.protocolVersion==="string"?e.params.protocolVersion:void 0);return}if(t.era==="modern"&&await I(e))return;t.instance.channel.deliver(e);return}if(!b(e)&&!x(e)){d(Error("Discarded a JSON-RPC response received before the connection negotiated an era"));return}let n=U(e);switch(n.kind){case"invalid-envelope":{let i=`Invalid _meta envelope for protocol revision 2026-07-28: ${n.issue.key}: ${n.issue.problem}`;if(b(e))await y(e.id,a.InvalidParams,i,{envelope:n.issue});else d(Error(`Discarded a notification with a malformed envelope: ${i}`));return}case"unsupported-revision":if(b(e)){let i=new D({supported:[...C],requested:n.requested});d(i),await y(e.id,i.code,i.message,i.data)}else d(Error(`Discarded a notification claiming unsupported protocol revision ${n.requested}`));return;case"modern":if(b(e)&&e.method==="server/discover"){if(t.phase==="probe"){t.instance.channel.deliver(e,{classification:n.classification});return}let i=await m("modern",n.revision);if(_()){await R(i);return}t={phase:"probe",instance:i},i.channel.deliver(e,{classification:n.classification});return}if(t.phase==="probe"){if(x(e)){t.instance.channel.deliver(e,{classification:n.classification});return}t={phase:"pinned",era:"modern",instance:t.instance}}else{let i=await m("modern",n.revision);if(_()){await R(i);return}t={phase:"pinned",era:"modern",instance:i}}if(await I(e))return;t.instance.channel.deliver(e,{classification:n.classification});return;case"legacy":{if(c==="reject"){if(b(e))await q(e,n.reason,n.requestedVersion);return}if(t.phase==="probe"){if(await N(t.instance),_())return;t={phase:"opening"}}let i=await m("legacy");if(_()){await R(i);return}t={phase:"pinned",era:"legacy",instance:i},t.instance.channel.deliver(e);return}}},S=[],E=!1,A=async()=>{if(E)return;E=!0;try{while(S.length>0){let e=S.shift();try{await W(e)}catch(n){if(b(e))await y(e.id,a.InternalError,"Internal server error");d(h(n))}}}finally{E=!1}},P=async()=>{if(p||t.phase==="closed")return;p=!0;let e=t;t={phase:"closed"};for(let n of w.teardownAll())await l.send(n).catch((i)=>d(h(i)));if(e.phase==="probe"||e.phase==="pinned")await e.instance.product.close().catch((n)=>d(h(n)));await l.close().catch((n)=>d(h(n)))};l.onmessage=(e)=>{S.push(e),A()},l.onerror=(e)=>{if(d(e),t.phase==="probe"||t.phase==="pinned")t.instance.channel.onerror?.(e)},l.onclose=()=>{if(p||t.phase==="closed")return;p=!0;let e=t;if(t={phase:"closed"},e.phase==="probe"||e.phase==="pinned")e.instance.product.close().catch((n)=>d(h(n)))};let j=l.start().catch((e)=>{throw d(h(e)),e});return j.catch(()=>{}),{close:async()=>{await j.catch(()=>{}),await P()}}}function h(r){return r instanceof Error?r:Error(String(r))}export{Q as serveStdio};
1
+ import{C,a,D,z,B,V,Z,b,x,H,re,ne,me,fe,he,ge,oe,se,ye,ve,be,O}from"./chunk-gcm6m7md.js";import"./chunk-txsgyj2d.js";var J=class{_readBuffer;_started=!1;_closed=!1;constructor(r=oe.stdin,o=oe.stdout,c){this._stdin=r,this._stdout=o,this._readBuffer=new he({maxBufferSize:c?.maxBufferSize})}onclose;onerror;onmessage;_ondata=(r)=>{try{this._readBuffer.append(r),this.processReadBuffer()}catch(o){this.onerror?.(o),this.close().catch(()=>{})}};_onerror=(r)=>{this.onerror?.(r)};_onstdouterror=(r)=>{this.onerror?.(r),this.close().catch(()=>{})};async start(){if(this._started)throw Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");this._started=!0,this._stdin.on("data",this._ondata),this._stdin.on("error",this._onerror),this._stdout.on("error",this._onstdouterror)}processReadBuffer(){while(!0)try{let r=this._readBuffer.readMessage();if(r===null)break;this.onmessage?.(r)}catch(r){this.onerror?.(r)}}async close(){if(this._closed)return;if(this._closed=!0,this._stdin.off("data",this._ondata),this._stdin.off("error",this._onerror),this._stdout.off("error",this._onstdouterror),this._stdin.listenerCount("data")===0)this._stdin.pause();this._readBuffer.clear(),this.onclose?.()}send(r){if(this._closed)return Promise.reject(Error("StdioServerTransport is closed"));return new Promise((o,c)=>{let l=ge(r),t=!1,f=(_)=>{if(t)return;t=!0,this._stdout.off("error",f),this._stdout.off("drain",p),c(_)},p=()=>{if(t)return;t=!0,this._stdout.off("error",f),this._stdout.off("drain",p),o()};if(this._stdout.once("error",f),this._stdout.write(l)){if(t)return;t=!0,this._stdout.off("error",f),o()}else if(!t)this._stdout.once("drain",p)})}},k=3000,L=class{onclose;onerror;onmessage;_closed=!1;_pendingRequests=new Set;_drainWaiters=[];constructor(r,o,c){this._wire=r,this._onInstanceClose=o,this._outboundIntercept=c}async start(){}async send(r,o){if(H(r)||re(r)){let{id:c}=r;if(c!==void 0)this._settle(c)}if(this._closed)return;if(this._outboundIntercept?.(r)==="handled")return;return this._wire.send(r,o)}setProtocolVersion=(r)=>{this._wire.setProtocolVersion?.(r)};deliver(r,o){if(this._closed)return;if(b(r))this._pendingRequests.add(r.id);else if(x(r)&&r.method==="notifications/cancelled"){let c=r.params?.requestId;if(c!==void 0)this._settle(c)}this.onmessage?.(r,o)}async whenRequestsAnswered(r){if(this._closed||this._pendingRequests.size===0)return!0;return await new Promise((o)=>{let c=()=>{clearTimeout(l),o(!0)},l=setTimeout(()=>{this._drainWaiters=this._drainWaiters.filter((t)=>t!==c),o(!1)},r);this._drainWaiters.push(c)})}async close(){if(this._closed)return;this._closed=!0,this._pendingRequests.clear(),this._releaseDrainWaiters();try{this._onInstanceClose()}finally{this.onclose?.()}}_settle(r){if(this._pendingRequests.delete(r),this._pendingRequests.size===0)this._releaseDrainWaiters()}_releaseDrainWaiters(){let r=this._drainWaiters;this._drainWaiters=[];for(let o of r)o()}};function U(r){let o=r.params;if(r.method==="initialize"&&!ne(o)){let f=o!==null&&typeof o==="object"&&typeof o.protocolVersion==="string"?o.protocolVersion:void 0;return{kind:"legacy",reason:"initialize",...f!==void 0&&{requestedVersion:f}}}if(!B(o))return{kind:"legacy",reason:"no-claim"};let c=z(o),l=(c===void 0?[]:Z(c))[0];if(l!==void 0)return{kind:"invalid-envelope",issue:l};let t=V(o);if(t===void 0||!C.includes(t))return{kind:"unsupported-revision",requested:t??"unknown"};return{kind:"modern",revision:t,classification:{era:"modern",revision:t}}}function Q(r,o={}){let c=o.legacy??"serve",l=o.transport??new J,t={phase:"opening"},f,p=!1,_=()=>p||t.phase==="closed",d=(e)=>{try{o.onerror?.(e)}catch{}},y=(e,n,i,s)=>l.send({jsonrpc:"2.0",id:e,error:{code:n,message:i,...s!==void 0&&{data:s}}}).catch((u)=>d(h(u))),w=new ye(o.maxSubscriptions??se),M=(e)=>{if(!x(e))return;let n=w.routeOutbound(e);if(n==="passthrough")return;for(let i of n)l.send({jsonrpc:"2.0",...i}).catch((s)=>d(h(s)));return"handled"},I=async(e)=>{if(b(e)&&e.method==="subscriptions/listen"){let n=z(e.params),i=B(e.params)?(n===void 0?[]:Z(n))[0]:{key:"_meta",problem:"the per-request envelope is required on protocol revision 2026-07-28"},s=V(e.params),u;if(i!==void 0)u={jsonrpc:"2.0",id:e.id,error:{code:-32602,message:`Invalid _meta envelope: ${i.key}: ${i.problem}`}};else if(s===void 0||!C.includes(s)){let v=new D({supported:[...C],requested:s??"unknown"});u={jsonrpc:"2.0",id:e.id,error:{code:v.code,message:v.message,data:v.data}}}else u=w.serve(e);return await l.send("error"in u?u:{jsonrpc:"2.0",method:u.method,params:u.params}).catch((v)=>d(h(v))),!0}if(x(e)&&e.method==="notifications/cancelled"){let n=e.params?.requestId;if(n!==void 0&&w.cancel(n))return!0}return!1},q=(e,n,i)=>{let s=me({kind:"legacy",reason:n,...i!==void 0&&{requestedVersion:i}},C);if(s===void 0)return Promise.resolve();return d(Error(`Rejected 2025-era request on a modern-only stdio connection (${s.cell}): ${s.message}`)),y(e.id,s.code,s.message,s.data)},T=(e)=>{if(p||e===f)return;P()},m=async(e,n)=>{let i=await r({era:e}),s=i instanceof O?i.server:i;if(e==="modern")fe(s,n),ve(s,C),w.setServerCapabilities(s.getCapabilities(),be(s));let u=new L(l,()=>T(u),e==="modern"?M:void 0);return await i.connect(u),{product:i,channel:u}},R=(e)=>e.product.close().catch((n)=>d(h(n))),N=async(e)=>{f=e.channel;try{if(!await e.channel.whenRequestsAnswered(k))d(Error(`Discarded the probe instance with requests still unanswered after ${k}ms; continuing with the fallback`));await e.product.close()}catch(n){d(h(n))}finally{f=void 0}},W=async(e)=>{if(t.phase==="closed")return;if(t.phase==="pinned"){if(t.era==="modern"&&b(e)&&e.method==="initialize"&&!ne(e.params)){await q(e,"initialize",e.params!==null&&typeof e.params==="object"&&typeof e.params.protocolVersion==="string"?e.params.protocolVersion:void 0);return}if(t.era==="modern"&&await I(e))return;t.instance.channel.deliver(e);return}if(!b(e)&&!x(e)){d(Error("Discarded a JSON-RPC response received before the connection negotiated an era"));return}let n=U(e);switch(n.kind){case"invalid-envelope":{let i=`Invalid _meta envelope for protocol revision 2026-07-28: ${n.issue.key}: ${n.issue.problem}`;if(b(e))await y(e.id,a.InvalidParams,i,{envelope:n.issue});else d(Error(`Discarded a notification with a malformed envelope: ${i}`));return}case"unsupported-revision":if(b(e)){let i=new D({supported:[...C],requested:n.requested});d(i),await y(e.id,i.code,i.message,i.data)}else d(Error(`Discarded a notification claiming unsupported protocol revision ${n.requested}`));return;case"modern":if(b(e)&&e.method==="server/discover"){if(t.phase==="probe"){t.instance.channel.deliver(e,{classification:n.classification});return}let i=await m("modern",n.revision);if(_()){await R(i);return}t={phase:"probe",instance:i},i.channel.deliver(e,{classification:n.classification});return}if(t.phase==="probe"){if(x(e)){t.instance.channel.deliver(e,{classification:n.classification});return}t={phase:"pinned",era:"modern",instance:t.instance}}else{let i=await m("modern",n.revision);if(_()){await R(i);return}t={phase:"pinned",era:"modern",instance:i}}if(await I(e))return;t.instance.channel.deliver(e,{classification:n.classification});return;case"legacy":{if(c==="reject"){if(b(e))await q(e,n.reason,n.requestedVersion);return}if(t.phase==="probe"){if(await N(t.instance),_())return;t={phase:"opening"}}let i=await m("legacy");if(_()){await R(i);return}t={phase:"pinned",era:"legacy",instance:i},t.instance.channel.deliver(e);return}}},S=[],E=!1,A=async()=>{if(E)return;E=!0;try{while(S.length>0){let e=S.shift();try{await W(e)}catch(n){if(b(e))await y(e.id,a.InternalError,"Internal server error");d(h(n))}}}finally{E=!1}},P=async()=>{if(p||t.phase==="closed")return;p=!0;let e=t;t={phase:"closed"};for(let n of w.teardownAll())await l.send(n).catch((i)=>d(h(i)));if(e.phase==="probe"||e.phase==="pinned")await e.instance.product.close().catch((n)=>d(h(n)));await l.close().catch((n)=>d(h(n)))};l.onmessage=(e)=>{S.push(e),A()},l.onerror=(e)=>{if(d(e),t.phase==="probe"||t.phase==="pinned")t.instance.channel.onerror?.(e)},l.onclose=()=>{if(p||t.phase==="closed")return;p=!0;let e=t;if(t={phase:"closed"},e.phase==="probe"||e.phase==="pinned")e.instance.product.close().catch((n)=>d(h(n)))};let j=l.start().catch((e)=>{throw d(h(e)),e});return j.catch(()=>{}),{close:async()=>{await j.catch(()=>{}),await P()}}}function h(r){return r instanceof Error?r:Error(String(r))}export{Q as serveStdio};
@@ -1 +1 @@
1
- import{O}from"./chunk-3ryp98a2.js";import{L,U}from"./chunk-mqsvqhkj.js";import"./chunk-s6j5t96c.js";import{e,o,i,l,s}from"./cli/index.js";var R={project:e().optional(),stage:e().optional()},C={sandboxId:e().min(1),reservationKey:e().min(1)},v=i().describe("Must be true, after the owner has agreed to this delete."),E="'********' keeps a stored secret and null deletes a field: never send a placeholder you did not read from a get.",M=[{singular:"agent",plural:"agents",key:"agentId",createHint:"Needs name and config; a 409 returns the existing agentId.",deleteHint:"Deleting an agent also drops its runtime rows.",updateHint:E,list:(t)=>t.listAgents(),get:(t,r)=>t.getAgent(r),create:(t,r)=>t.createAgent(r),update:(t,r,n)=>t.updateAgent(r,n),remove:(t,r)=>t.deleteAgent(r)},{singular:"cron",plural:"crons",key:"cronId",createHint:"Needs name, agentId, scheduleExpression (rate/cron/at) and input or events.",list:(t)=>t.listCrons(),get:(t,r)=>t.getCron(r),create:(t,r)=>t.createCron(r),update:(t,r,n)=>t.updateCron(r,n),remove:(t,r)=>t.deleteCron(r)},{singular:"sandbox",plural:"sandboxes",key:"sandboxId",updateHint:E,list:(t)=>t.listSandboxes(),get:(t,r)=>t.getSandbox(r),create:(t,r)=>t.createSandbox(r),update:(t,r,n)=>t.updateSandbox(r,n),remove:(t,r)=>t.deleteSandbox(r)},{singular:"workspace",plural:"workspaces",key:"workspaceId",list:(t)=>t.listWorkspaces(),get:(t,r)=>t.getWorkspace(r),create:(t,r)=>t.createWorkspace(r),update:(t,r,n)=>t.updateWorkspace(r,n),remove:(t,r)=>t.deleteWorkspace(r)},{singular:"policy",plural:"policies",key:"policyId",createHint:"Agent-runtime policy document (tool.call, skill.load), not API credential scoping.",list:(t)=>t.listPolicies(),get:(t,r)=>t.getPolicy(r),create:(t,r)=>t.createPolicy(r),update:(t,r,n)=>t.updatePolicy(r,n),remove:(t,r)=>t.deletePolicy(r)},{singular:"role",plural:"roles",key:"roleId",createHint:"Needs name and a version-1 policy over the API namespace, e.g. agents:read. Account secret only.",list:(t)=>t.listRoles(),get:(t,r)=>t.getRole(r),create:(t,r)=>t.createRole(r),update:(t,r,n)=>t.updateRole(r,n),remove:(t,r)=>t.deleteRole(r)},{singular:"channel",plural:"channels",key:"channelId",list:(t)=>t.listChannels(),get:(t,r)=>t.getChannel(r),create:(t,r)=>t.createChannel(r),update:(t,r,n)=>t.updateChannel(r,n),remove:(t,r)=>t.deleteChannel(r)},{singular:"skill",plural:"skills",key:"skillName",createHint:'source is "files" (base64), "json" or "github". The stored name comes from the SKILL.md frontmatter.',list:(t)=>t.listSkills(),get:(t,r)=>t.getSkill(r),create:(t,r)=>t.createSkill(r),remove:(t,r)=>t.deleteSkill(r)},{singular:"mcp",plural:"mcp",key:"serverId",scoped:!0,list:(t,r)=>t.listMcp(r),get:(t,r)=>t.getMcp(r),create:(t,r,n)=>t.createMcp(n,r),update:(t,r,n)=>t.updateMcp(r,n),remove:(t,r)=>t.deleteMcp(r)}];function Pt(t={}){let r=L()?new U({}):null;if(!r&&!t.cli)throw Error("broods mcp needs a credential: set BROODS_SESSION_TOKEN or BROODS_ACCOUNT_SECRET, or run `broods login`.");let n=new O({name:"broods",version:"1"},{capabilities:{tools:{}}});if(r){for(let a of M)N(n,r,a,t.scope??{});j(n,r)}if(t.cli)P(n,t.cli);if(t.allowDestructive)q(n,r,t.cli??null);return n}function w(t,r){if(!t)throw Error(`Refusing without confirm:true. ${r}`)}async function d(t){try{return k(await t())}catch(r){return A(r)}}function A(t){return{content:[{type:"text",text:t instanceof Error?t.message:String(t)}],isError:!0}}function k(t){return{content:[{type:"text",text:JSON.stringify(t??null,null,2)}]}}function b(t,r,n,a){if(!t.scoped)return;let c=n??r.project,p=a??r.stage;if(!c||!p)throw Error(`${t.plural} live in one stage, so this call needs both 'project' and 'stage'`);return{project:c,stage:p}}function P(t,r){t.registerTool("list-orgs",{description:"Every organization this login can reach, with the current one marked.",inputSchema:{}},async()=>await d(async()=>{let n=await r.getOnboarding();return{currentOrgId:n.currentOrgId,orgs:n.orgs}})),t.registerTool("create-org",{description:"Create an organization and switch this login to it. Later calls act in the new org.",inputSchema:{name:e().min(1)}},async({name:n})=>await d(async()=>await r.createOnboardingOrg(n))),t.registerTool("select-org",{description:"Switch this login to another organization, by an orgId from list-orgs.",inputSchema:{orgId:e().min(1)}},async({orgId:n})=>await d(async()=>await r.selectOnboardingOrg(n))),t.registerTool("list-projects",{description:"Every project in the current org, empty ones last. Project names are not unique, so take the id from here.",inputSchema:{}},async()=>await d(async()=>await r.listProjects())),t.registerTool("list-stages",{description:"Every stage of one project, by project name.",inputSchema:{project:e().min(1)}},async({project:n})=>await d(async()=>await r.listStages(n))),t.registerTool("create-stage",{description:"Create a stage in a project, optionally cloning another stage's architecture and env vars. A project name that does not exist yet is created with it, which is the only way to make a project without deploying a manifest.",inputSchema:{project:e().min(1),name:e().min(1),from:e().optional().describe("Stage to clone architecture and env vars from.")}},async({project:n,name:a,from:c})=>await d(async()=>await r.createStage(n,a,c)))}function q(t,r,n){if(r)t.registerTool("rotate-secret",{description:"Rotate the account secret. The current secret stops working immediately and the new one is shown once, so this breaks every deployment and CI job still holding the old one. Requires confirm:true.",inputSchema:{confirm:v}},async({confirm:a})=>await d(async()=>(w(a,"Everything holding the current secret breaks the moment this runs."),await r.rotateSecret())));if(n)t.registerTool("delete-project",{description:"Delete a project and everything under it: stages, agents, canvas, env vars, crons and workspace files. Takes a projectId from list-projects and requires confirm:true.",inputSchema:{projectId:e().min(1),confirm:v}},async({projectId:a,confirm:c})=>await d(async()=>(w(c,`Project '${a}' and everything under it goes away: name it, get the owner's agreement, then retry.`),await n.deleteProject(a))))}function j(t,r){t.registerTool("list-cron-runs",{description:"Run history for one cron, newest first. Check this before telling anyone a cron fired.",inputSchema:{cronId:e().min(1),limit:o().int().positive().max(100).optional()}},async({cronId:a,limit:c})=>await d(async()=>await r.listCronRuns(a,c===void 0?{}:{limit:c})));let n=[["suspend-sandbox","Suspend a persistent sandbox reservation.",(a,c)=>r.suspendSandbox(a,c)],["resume-sandbox","Resume a suspended sandbox reservation.",(a,c)=>r.resumeSandbox(a,c)],["terminate-sandbox","Terminate a sandbox reservation and drop its live-instance row. Terminate only a sandbox you created, and never one holding state you did not put there.",(a,c)=>r.terminateSandbox(a,c)],["open-sandbox-terminal","Mint a short-lived sealed ticket for an interactive PTY session on a persistent sandbox.",(a,c)=>r.openSandboxTerminal(a,c)]];for(let[a,c,p]of n)t.registerTool(a,{description:c,inputSchema:C},async({sandboxId:f,reservationKey:g})=>await d(async()=>await p(f,g)));t.registerTool("snapshot-sandbox",{description:"Snapshot a sandbox reservation into a reusable image (self-hosted provider).",inputSchema:{sandboxId:e().min(1),reservationKey:e().min(1),name:e().min(1)}},async({sandboxId:a,reservationKey:c,name:p})=>await d(async()=>await r.snapshotSandbox(a,c,p))),t.registerTool("upload-skill",{description:"Replace one skill's bundle wholesale (PUT, not a merge) by skillName. 'body' is the same source shape create-skill takes.",inputSchema:{id:e().min(1).describe("The skillName."),body:s(e(),l())}},async({id:a,body:c})=>await d(async()=>await r.uploadSkill(a,c))),t.registerTool("list-env-vars",{description:"List environment variable names. Values are write-only: the plane never returns them, so there is no read.",inputSchema:{}},async()=>await d(async()=>await r.listEnvVars())),t.registerTool("set-env-var",{description:"Create or replace one write-only environment variable. Reference it from a config as ${NAME} rather than inlining the value.",inputSchema:{name:e().min(1),value:e()}},async({name:a,value:c})=>await d(async()=>(await r.setEnvVar(a,c),{name:a,stored:!0}))),t.registerTool("delete-env-var",{description:"Delete one environment variable.",inputSchema:{name:e().min(1),confirm:v}},async({name:a,confirm:c})=>await d(async()=>(w(c,`Anything referencing \${${a}} breaks the moment this env var goes.`),{deleted:await r.deleteEnvVar(a),name:a}))),t.registerTool("assume-role",{description:"Exchange a role for a short-lived session token (default 1h, max 12h). Hand the returned token to whatever tool needs narrow access, as BROODS_SESSION_TOKEN. It is shown once and is not stored here.",inputSchema:{roleId:e().min(1),ttlSeconds:o().int().positive().max(43200).optional()}},async({roleId:a,ttlSeconds:c})=>await d(async()=>await r.assumeRole(a,c===void 0?{}:{ttlSeconds:c}))),t.registerTool("update-account",{description:"Rename this account or change its description. Everything else about an account is derived, not settable.",inputSchema:{username:e().optional(),description:e().nullable().optional()}},async({username:a,description:c})=>await d(async()=>await r.updateAccount({...a===void 0?{}:{username:a},...c===void 0?{}:{description:c}}))),t.registerTool("get-account",{description:"The account this server's credential resolves to. Run it first to confirm which tenant you are about to change.",inputSchema:{}},async()=>await d(async()=>await r.getAccount()))}function N(t,r,n,a){let c=e().min(1).describe(`The ${n.key}.`),p=n.scoped?` ${n.plural} live in one stage, so this also takes 'project' and 'stage'.`:"",{list:f,get:g,create:y,update:S,remove:_}=n;if(f){let u=`List every ${n.singular} on the account.${p}`;if(n.scoped)t.registerTool(`list-${n.plural}`,{description:u,inputSchema:R},async({project:h,stage:m})=>await d(async()=>await f(r,b(n,a,h,m))));else t.registerTool(`list-${n.plural}`,{description:u,inputSchema:{}},async()=>await d(async()=>await f(r)))}if(g)t.registerTool(`get-${n.singular}`,{description:`Read one ${n.singular} by ${n.key}.`,inputSchema:{id:c}},async({id:u})=>await d(async()=>await g(r,u)));if(y){let u=`Create one ${n.singular}. 'body' is the request body the config plane documents.`+(n.createHint?` ${n.createHint}`:"")+p+" Prefer changing the broods/ manifest and deploying for anything the project already declares.",h=s(e(),l());if(n.scoped)t.registerTool(`create-${n.singular}`,{description:u,inputSchema:{body:h,...R}},async({body:m,project:T,stage:I})=>await d(async()=>await y(r,m,b(n,a,T,I))));else t.registerTool(`create-${n.singular}`,{description:u,inputSchema:{body:h}},async({body:m})=>await d(async()=>await y(r,m)))}if(S)t.registerTool(`update-${n.singular}`,{description:`Deep-merge a patch into one ${n.singular} and return the updated record, so there is no need to read it back.`+(n.updateHint?` ${n.updateHint}`:""),inputSchema:{id:c,body:s(e(),l())}},async({id:u,body:h})=>await d(async()=>await S(r,u,h)));if(_)t.registerTool(`delete-${n.singular}`,{description:`Delete one ${n.singular}. Requires confirm:true, and takes one ${n.key} per call: never loop this over a list.`+(n.deleteHint?` ${n.deleteHint}`:""),inputSchema:{id:c,confirm:v}},async({id:u,confirm:h})=>await d(async()=>(w(h,`Deleting ${n.singular} '${u}' goes away for good: name it, get the owner's agreement, then retry.`),{deleted:await _(r,u),id:u})))}export{Pt as createBroodsMcpServer};
1
+ import{O}from"./chunk-gcm6m7md.js";import{L,U}from"./chunk-mqsvqhkj.js";import"./chunk-txsgyj2d.js";import{e,o,i,l,s}from"./cli/index.js";var R={project:e().optional(),stage:e().optional()},C={sandboxId:e().min(1),reservationKey:e().min(1)},v=i().describe("Must be true, after the owner has agreed to this delete."),E="'********' keeps a stored secret and null deletes a field: never send a placeholder you did not read from a get.",M=[{singular:"agent",plural:"agents",key:"agentId",createHint:"Needs name and config; a 409 returns the existing agentId.",deleteHint:"Deleting an agent also drops its runtime rows.",updateHint:E,list:(t)=>t.listAgents(),get:(t,r)=>t.getAgent(r),create:(t,r)=>t.createAgent(r),update:(t,r,n)=>t.updateAgent(r,n),remove:(t,r)=>t.deleteAgent(r)},{singular:"cron",plural:"crons",key:"cronId",createHint:"Needs name, agentId, scheduleExpression (rate/cron/at) and input or events.",list:(t)=>t.listCrons(),get:(t,r)=>t.getCron(r),create:(t,r)=>t.createCron(r),update:(t,r,n)=>t.updateCron(r,n),remove:(t,r)=>t.deleteCron(r)},{singular:"sandbox",plural:"sandboxes",key:"sandboxId",updateHint:E,list:(t)=>t.listSandboxes(),get:(t,r)=>t.getSandbox(r),create:(t,r)=>t.createSandbox(r),update:(t,r,n)=>t.updateSandbox(r,n),remove:(t,r)=>t.deleteSandbox(r)},{singular:"workspace",plural:"workspaces",key:"workspaceId",list:(t)=>t.listWorkspaces(),get:(t,r)=>t.getWorkspace(r),create:(t,r)=>t.createWorkspace(r),update:(t,r,n)=>t.updateWorkspace(r,n),remove:(t,r)=>t.deleteWorkspace(r)},{singular:"policy",plural:"policies",key:"policyId",createHint:"Agent-runtime policy document (tool.call, skill.load), not API credential scoping.",list:(t)=>t.listPolicies(),get:(t,r)=>t.getPolicy(r),create:(t,r)=>t.createPolicy(r),update:(t,r,n)=>t.updatePolicy(r,n),remove:(t,r)=>t.deletePolicy(r)},{singular:"role",plural:"roles",key:"roleId",createHint:"Needs name and a version-1 policy over the API namespace, e.g. agents:read. Account secret only.",list:(t)=>t.listRoles(),get:(t,r)=>t.getRole(r),create:(t,r)=>t.createRole(r),update:(t,r,n)=>t.updateRole(r,n),remove:(t,r)=>t.deleteRole(r)},{singular:"channel",plural:"channels",key:"channelId",list:(t)=>t.listChannels(),get:(t,r)=>t.getChannel(r),create:(t,r)=>t.createChannel(r),update:(t,r,n)=>t.updateChannel(r,n),remove:(t,r)=>t.deleteChannel(r)},{singular:"skill",plural:"skills",key:"skillName",createHint:'source is "files" (base64), "json" or "github". The stored name comes from the SKILL.md frontmatter.',list:(t)=>t.listSkills(),get:(t,r)=>t.getSkill(r),create:(t,r)=>t.createSkill(r),remove:(t,r)=>t.deleteSkill(r)},{singular:"mcp",plural:"mcp",key:"serverId",scoped:!0,list:(t,r)=>t.listMcp(r),get:(t,r)=>t.getMcp(r),create:(t,r,n)=>t.createMcp(n,r),update:(t,r,n)=>t.updateMcp(r,n),remove:(t,r)=>t.deleteMcp(r)}];function Pt(t={}){let r=L()?new U({}):null;if(!r&&!t.cli)throw Error("broods mcp needs a credential: set BROODS_SESSION_TOKEN or BROODS_ACCOUNT_SECRET, or run `broods login`.");let n=new O({name:"broods",version:"1"},{capabilities:{tools:{}}});if(r){for(let a of M)N(n,r,a,t.scope??{});j(n,r)}if(t.cli)P(n,t.cli);if(t.allowDestructive)q(n,r,t.cli??null);return n}function w(t,r){if(!t)throw Error(`Refusing without confirm:true. ${r}`)}async function d(t){try{return k(await t())}catch(r){return A(r)}}function A(t){return{content:[{type:"text",text:t instanceof Error?t.message:String(t)}],isError:!0}}function k(t){return{content:[{type:"text",text:JSON.stringify(t??null,null,2)}]}}function b(t,r,n,a){if(!t.scoped)return;let c=n??r.project,p=a??r.stage;if(!c||!p)throw Error(`${t.plural} live in one stage, so this call needs both 'project' and 'stage'`);return{project:c,stage:p}}function P(t,r){t.registerTool("list-orgs",{description:"Every organization this login can reach, with the current one marked.",inputSchema:{}},async()=>await d(async()=>{let n=await r.getOnboarding();return{currentOrgId:n.currentOrgId,orgs:n.orgs}})),t.registerTool("create-org",{description:"Create an organization and switch this login to it. Later calls act in the new org.",inputSchema:{name:e().min(1)}},async({name:n})=>await d(async()=>await r.createOnboardingOrg(n))),t.registerTool("select-org",{description:"Switch this login to another organization, by an orgId from list-orgs.",inputSchema:{orgId:e().min(1)}},async({orgId:n})=>await d(async()=>await r.selectOnboardingOrg(n))),t.registerTool("list-projects",{description:"Every project in the current org, empty ones last. Project names are not unique, so take the id from here.",inputSchema:{}},async()=>await d(async()=>await r.listProjects())),t.registerTool("list-stages",{description:"Every stage of one project, by project name.",inputSchema:{project:e().min(1)}},async({project:n})=>await d(async()=>await r.listStages(n))),t.registerTool("create-stage",{description:"Create a stage in a project, optionally cloning another stage's architecture and env vars. A project name that does not exist yet is created with it, which is the only way to make a project without deploying a manifest.",inputSchema:{project:e().min(1),name:e().min(1),from:e().optional().describe("Stage to clone architecture and env vars from.")}},async({project:n,name:a,from:c})=>await d(async()=>await r.createStage(n,a,c)))}function q(t,r,n){if(r)t.registerTool("rotate-secret",{description:"Rotate the account secret. The current secret stops working immediately and the new one is shown once, so this breaks every deployment and CI job still holding the old one. Requires confirm:true.",inputSchema:{confirm:v}},async({confirm:a})=>await d(async()=>(w(a,"Everything holding the current secret breaks the moment this runs."),await r.rotateSecret())));if(n)t.registerTool("delete-project",{description:"Delete a project and everything under it: stages, agents, canvas, env vars, crons and workspace files. Takes a projectId from list-projects and requires confirm:true.",inputSchema:{projectId:e().min(1),confirm:v}},async({projectId:a,confirm:c})=>await d(async()=>(w(c,`Project '${a}' and everything under it goes away: name it, get the owner's agreement, then retry.`),await n.deleteProject(a))))}function j(t,r){t.registerTool("list-cron-runs",{description:"Run history for one cron, newest first. Check this before telling anyone a cron fired.",inputSchema:{cronId:e().min(1),limit:o().int().positive().max(100).optional()}},async({cronId:a,limit:c})=>await d(async()=>await r.listCronRuns(a,c===void 0?{}:{limit:c})));let n=[["suspend-sandbox","Suspend a persistent sandbox reservation.",(a,c)=>r.suspendSandbox(a,c)],["resume-sandbox","Resume a suspended sandbox reservation.",(a,c)=>r.resumeSandbox(a,c)],["terminate-sandbox","Terminate a sandbox reservation and drop its live-instance row. Terminate only a sandbox you created, and never one holding state you did not put there.",(a,c)=>r.terminateSandbox(a,c)],["open-sandbox-terminal","Mint a short-lived sealed ticket for an interactive PTY session on a persistent sandbox.",(a,c)=>r.openSandboxTerminal(a,c)]];for(let[a,c,p]of n)t.registerTool(a,{description:c,inputSchema:C},async({sandboxId:f,reservationKey:g})=>await d(async()=>await p(f,g)));t.registerTool("snapshot-sandbox",{description:"Snapshot a sandbox reservation into a reusable image (self-hosted provider).",inputSchema:{sandboxId:e().min(1),reservationKey:e().min(1),name:e().min(1)}},async({sandboxId:a,reservationKey:c,name:p})=>await d(async()=>await r.snapshotSandbox(a,c,p))),t.registerTool("upload-skill",{description:"Replace one skill's bundle wholesale (PUT, not a merge) by skillName. 'body' is the same source shape create-skill takes.",inputSchema:{id:e().min(1).describe("The skillName."),body:s(e(),l())}},async({id:a,body:c})=>await d(async()=>await r.uploadSkill(a,c))),t.registerTool("list-env-vars",{description:"List environment variable names. Values are write-only: the plane never returns them, so there is no read.",inputSchema:{}},async()=>await d(async()=>await r.listEnvVars())),t.registerTool("set-env-var",{description:"Create or replace one write-only environment variable. Reference it from a config as ${NAME} rather than inlining the value.",inputSchema:{name:e().min(1),value:e()}},async({name:a,value:c})=>await d(async()=>(await r.setEnvVar(a,c),{name:a,stored:!0}))),t.registerTool("delete-env-var",{description:"Delete one environment variable.",inputSchema:{name:e().min(1),confirm:v}},async({name:a,confirm:c})=>await d(async()=>(w(c,`Anything referencing \${${a}} breaks the moment this env var goes.`),{deleted:await r.deleteEnvVar(a),name:a}))),t.registerTool("assume-role",{description:"Exchange a role for a short-lived session token (default 1h, max 12h). Hand the returned token to whatever tool needs narrow access, as BROODS_SESSION_TOKEN. It is shown once and is not stored here.",inputSchema:{roleId:e().min(1),ttlSeconds:o().int().positive().max(43200).optional()}},async({roleId:a,ttlSeconds:c})=>await d(async()=>await r.assumeRole(a,c===void 0?{}:{ttlSeconds:c}))),t.registerTool("update-account",{description:"Rename this account or change its description. Everything else about an account is derived, not settable.",inputSchema:{username:e().optional(),description:e().nullable().optional()}},async({username:a,description:c})=>await d(async()=>await r.updateAccount({...a===void 0?{}:{username:a},...c===void 0?{}:{description:c}}))),t.registerTool("get-account",{description:"The account this server's credential resolves to. Run it first to confirm which tenant you are about to change.",inputSchema:{}},async()=>await d(async()=>await r.getAccount()))}function N(t,r,n,a){let c=e().min(1).describe(`The ${n.key}.`),p=n.scoped?` ${n.plural} live in one stage, so this also takes 'project' and 'stage'.`:"",{list:f,get:g,create:y,update:S,remove:_}=n;if(f){let u=`List every ${n.singular} on the account.${p}`;if(n.scoped)t.registerTool(`list-${n.plural}`,{description:u,inputSchema:R},async({project:h,stage:m})=>await d(async()=>await f(r,b(n,a,h,m))));else t.registerTool(`list-${n.plural}`,{description:u,inputSchema:{}},async()=>await d(async()=>await f(r)))}if(g)t.registerTool(`get-${n.singular}`,{description:`Read one ${n.singular} by ${n.key}.`,inputSchema:{id:c}},async({id:u})=>await d(async()=>await g(r,u)));if(y){let u=`Create one ${n.singular}. 'body' is the request body the config plane documents.`+(n.createHint?` ${n.createHint}`:"")+p+" Prefer changing the broods/ manifest and deploying for anything the project already declares.",h=s(e(),l());if(n.scoped)t.registerTool(`create-${n.singular}`,{description:u,inputSchema:{body:h,...R}},async({body:m,project:T,stage:I})=>await d(async()=>await y(r,m,b(n,a,T,I))));else t.registerTool(`create-${n.singular}`,{description:u,inputSchema:{body:h}},async({body:m})=>await d(async()=>await y(r,m)))}if(S)t.registerTool(`update-${n.singular}`,{description:`Deep-merge a patch into one ${n.singular} and return the updated record, so there is no need to read it back.`+(n.updateHint?` ${n.updateHint}`:""),inputSchema:{id:c,body:s(e(),l())}},async({id:u,body:h})=>await d(async()=>await S(r,u,h)));if(_)t.registerTool(`delete-${n.singular}`,{description:`Delete one ${n.singular}. Requires confirm:true, and takes one ${n.key} per call: never loop this over a list.`+(n.deleteHint?` ${n.deleteHint}`:""),inputSchema:{id:c,confirm:v}},async({id:u,confirm:h})=>await d(async()=>(w(h,`Deleting ${n.singular} '${u}' goes away for good: name it, get the owner's agreement, then retry.`),{deleted:await _(r,u),id:u})))}export{Pt as createBroodsMcpServer};
@@ -1,5 +1,5 @@
1
1
  function W(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*Se(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()}}var Me=500,ge=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/,le=/^(?!0{32}$)[0-9a-f]{32}$/;function xe(e){return e==="DEBUG"||e==="INFO"||e==="WARN"||e==="ERROR"}function Ae(e){if(typeof e!=="object"||e===null)return!1;let n=e;if(n.type==="subscribe")return de(n);if(n.type==="unsubscribe")return D(n.stream);if(n.type==="fetchTrace")return $e(n.traceId);return!1}function ze(e){return e==="task"||e==="cron"||e==="subtask"}function G(e){return typeof e==="string"&&ge.test(e)}function $e(e){return typeof e==="string"&&le.test(e)}function D(e){return e==="logs"||e==="traces"}function de(e){let n=e.stream;if(!D(n))return!1;let t=e.backfill;if(t!==void 0&&(typeof t!=="number"||!Number.isSafeInteger(t)||t<0||t>500))return!1;if(e.liveOnly!==void 0&&typeof e.liveOnly!=="boolean")return!1;let o=e.minLevel;if(o!==void 0&&!xe(o))return!1;let r=e.sandboxId;return r===void 0||n==="logs"&&G(r)}import{mkdir as pe,readFile as fe,writeFile as ye}from"node:fs/promises";import{homedir as me}from"node:os";import{dirname as be,join as Re}from"node:path";var v="broods",w="_generated",k=Re(me(),".broods","config.json"),T="broods.app",M="dashboard",he="gateway";async function K(){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 fe(k,"utf8"));if(typeof t.baseUrl!=="string"||!t.baseUrl)return null;return t}catch{return null}}async function J(e){await pe(be(k),{recursive:!0}),await ye(k,`${JSON.stringify(e,null,2)}
3
- `,"utf8")}function _(){return process.env.BROODS_STAGE}function g(e){let n=e.length;while(n>0&&e.charCodeAt(n-1)===47)n-=1;return e.slice(0,n)}function ae(e){try{let n=new URL(e);if(!n.hostname.startsWith(`${M}.`))return;let t=n.hostname.slice(M.length+1);if(t!==T&&!t.endsWith(`.${T}`))return;return n.hostname=`${he}.${t}`,g(n.origin)}catch{return}}import{existsSync as B,readFileSync as O,statSync as Ce}from"node:fs";import{join as H,resolve as L}from"node:path";var E=new Map,Ie=new Set(Object.keys(process.env)),U=new Set;function f(e=process.cwd()){ve(e);let n=Te();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,stage:_()}}function N(e,n=process.cwd()){let t=process.env[e];if(t===void 0)return!1;let o=L(n);for(let r of[".env.local",".env"]){let s=H(o,r);if(!B(s))continue;let i=j(O(s,"utf8"));if(i[e]!==void 0)return i[e]!==t}return!0}function ve(e){let n=L(e),t=process.env.BROODS_RELOAD_ENV==="1";for(let o of[".env",".env.local"]){let r=H(n,o);if(!B(r)){E.delete(r);continue}let s=Ce(r).mtimeMs,i=E.get(r);if(i&&i.mtimeMs===s)continue;E.set(r,{mtimeMs:s});for(let[a,u]of Object.entries(j(O(r,"utf8"))))if(t||!Ie.has(a)||U.has(a))process.env[a]=u,U.add(a)}}function j(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]=Ee(r.slice(s+1).trim())}return n}function Ee(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 Te(){try{let e=JSON.parse(O(k,"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 P="https://gateway.broods.app";class we extends Error{accepted;constructor(e){super(`Ingress ${e.eventId} was accepted asynchronously with status ${e.status??"queued"}`);this.accepted=e;this.name="IngressAcceptedError"}}class F{baseUrl;apiKey;fetchImpl;constructor(e={}){f(),this.baseUrl=Y(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||P),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}/v1/webhooks/${t.join("/")}`}stageWebhookUrl(e,n,t){let o=[e,"dev",n,t].map(encodeURIComponent);return`${this.baseUrl}/v1/webhooks/${o.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}),continue:(s)=>this.continue({...s,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),continue:(o)=>this.continue(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=q(t,"cli"),r=n?this.scopedUrl(e):`${this.baseUrl}/v1/runs`,s=await this.openStream(o,r);if(!s.ok)throw Error(`Run failed: ${s.status} ${await s.text()}`);if(s.status===202)throw new we(Z(await s.json(),o));if(!s.body)throw Error("Run response has no body");for await(let i of Se(s.body)){let a;try{a=JSON.parse(i)}catch{continue}if(a.type==="error")throw Error(`Agent run failed: ${Pe(a.error)}`);yield a}}async runAsync(e,n){let t=n?{...n,agentId:e.id,agentName:e.name}:e,o={...q(t,"async"),background:!0},r=n?this.scopedUrl(e):`${this.baseUrl}/v1/runs`;return this.postAcceptedRun(r,o,"Async run")}async continue(e,n){let t=n?e:null,o=n??e,r={agentId:t?t.id:e.agentId,eventId:o.eventId??`continue-${crypto.randomUUID()}`,conversationKey:o.conversationKey,continue:!0},s=t?this.scopedUrl(t):`${this.baseUrl}/v1/runs`;return this.postAcceptedRun(s,r,"Continue")}async getAsyncStatus(e){let n=this.resolveStatusUrl(e),t=await this.fetchJson(n,{method:"GET",headers:this.apiKeyHeaders()});if(t.status===404)return{status:"not_found"};if(!t.ok)throw Error(`Status check failed: ${t.status} ${await t.text()}`);return await t.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);if(r.status==="awaiting_approval"||r.status==="awaiting_input"||r.status==="completed"||r.status==="failed"||r.status==="expired"||r.status==="not_found")return r;await tn(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(en(e))});if(n.status!==201)throw Error(`Create cron job failed: ${n.status} ${await n.text()}`);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 e.text()}`);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 n.text()}`);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 r.text()}`);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 t.text()}`);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 n.text()}`);return(await n.json()).deleted}scopedUrl(e){if(e.projectSlug&&e.stageSlug&&e.endpointId)return`${this.baseUrl}/v1/projects/${encodeURIComponent(e.projectSlug)}/stages/${encodeURIComponent(e.stageSlug)}/agents/${encodeURIComponent(e.endpointId)}`;return`${this.baseUrl}/v1/runs`}asyncAgentRun(e,n){return{...e,conversationKey:n,poll:()=>this.getAsyncStatus(e),wait:(t)=>this.waitForAsyncStatus(e,t)}}async postAcceptedRun(e,n,t){let o=await this.fetchJson(e,{method:"POST",headers:this.apiKeyHeaders(),body:JSON.stringify(n)});if(o.status!==202)throw Error(`${t} failed: ${o.status} ${await nn(o,"202 JSON")}`);return this.asyncAgentRun(Z(await o.json(),n),n.conversationKey)}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 ${P} 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){if(typeof e!=="string")return e.statusUrl;if(/^https?:\/\//.test(e))return e;return on(this.baseUrl,e)}}function Y(e){let n=e.trim(),t=/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(n)?n:`https://${n}`;return g(t)}function q(e,n){let t=e.eventId??`${n}-${Date.now()}`;return{agentId:e.agentId,eventId:t,conversationKey:e.conversationKey??t,events:W(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 Pe(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}`}function Z(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=typeof e.runId==="string"?e.runId:_e(t);if(!o)throw Error("Async response missing runId");let r=typeof e.eventId==="string"?e.eventId:o,{status:s,requestedMode:i}=e;return{statusUrl:t,runId:o,eventId:r,agentId:n.agentId,...s==="accepted"||s==="queued"||s==="applied"||s==="processing"?{status:s}:{},...i==="reject"||i==="followup"||i==="collect"||i==="steer"?{requestedMode:i}:{}}}function _e(e){let n=new URL(e).pathname.match(/\/v1\/runs\/([^/]+)$/);return n?.[1]?decodeURIComponent(n[1]):void 0}function en(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}}async function nn(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 ignored the background flag and ran the request on the direct streaming runner.`;let o=await e.text();if(o.length<=2000)return o;return`${o.slice(0,2000)}... [truncated ${o.length-2000} chars]`}function tn(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 on(e,n){return`${Y(e)}/v1/runs/${encodeURIComponent(n)}`}var rn=2000,z=0,b=1;class ke{baseUrl;apiKey;WebSocketImpl;connectTimeoutMs;constructor(e={}){if(f(),this.baseUrl=te(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||P),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??rn}subscribe(e,n={}){let t=this.resolveWebSocket(),o=this.buildUrl(e),r=sn(e),s=ne(this.baseUrl),i=new t(o,I(this.apiKey)),a=!1,u=!1,l=setTimeout(()=>{if(!a)m(s)},this.connectTimeoutMs),x=(c=1000,S="client closed")=>{if(u)return;if(u=!0,clearTimeout(l),e.signal?.removeEventListener("abort",C),i.readyState===b||i.readyState===z)i.close(c,S)},m=(c)=>{n.onError?.(c),x(1011,"websocket error")},ue=()=>{n.onDone?.(),x(1000,"done")},C=()=>{if(i.readyState===b)i.send(JSON.stringify({type:"cancel"}));m(Error("WebSocket run aborted."))};return e.signal?.addEventListener("abort",C,{once:!0}),i.onopen=()=>{if(a=!0,clearTimeout(l),e.signal?.aborted){C();return}i.send(JSON.stringify({type:"execute",agentId:r,...e.answers?{answers:e.answers}:{events:W(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 S=Q(c.data);if(!S)return;let A=ee(S,n);switch(n.onMessage?.(A),A.type){case"meta":n.onMeta?.(A);break;case"done":ue();break;case"error":m(Error(V(A.error)));break}},i.onerror=()=>{m(s)},i.onclose=(c)=>{if(clearTimeout(l),e.signal?.removeEventListener("abort",C),u||c.code===1000)return;m(Error(c.reason||"WebSocket connection closed."))},{url:o,sendControl:function(c){if(i.readyState!==b)throw Error("WebSocket is not open");i.send(JSON.stringify({type:"control",...c}))},close:x}}attach(e,n={}){let t=this.resolveWebSocket(),o=this.buildUrl(e),r=new t(o,I(this.apiKey)),s=!1,i=(a=1000,u="client closed")=>{if(s)return;if(s=!0,r.readyState===b||r.readyState===z)r.close(a,u)};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 u=Q(a.data);if(!u)return;let l=ee(u,n);if(n.onMessage?.(l),l.type==="done")n.onDone?.();if(l.type==="error")n.onError?.(Error(V(l.error)))},r.onerror=()=>n.onError?.(ne(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!==b)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=an(e),t=e.projectSlug??e.agent?.projectSlug,o=e.stageSlug??e.agent?.stageSlug,r=t&&o?`/projects/${encodeURIComponent(t)}/stages/${encodeURIComponent(o)}`:"";return`${X(this.baseUrl)}/v1${r}/agents/${encodeURIComponent(n)}/ws`}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 X(e){let n=new URL(te(e));if(n.protocol==="https:")n.protocol="wss:";if(n.protocol==="http:")n.protocol="ws:";return g(n.toString())}function I(e){return["broods.v1",`broods.token.${e}`]}function V(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)}function te(e){let n=e.trim();if(/^wss?:\/\//.test(n))return g(n);return Y(n)}function Q(e){try{let n=JSON.parse(e);return typeof n==="object"&&n!==null&&typeof n.type==="string"?n:null}catch{return null}}function sn(e){let n=e.agentId??e.agent?.id??e.endpointId;if(!n)throw Error("WebSocket run requires agent, agentId, or endpointId.");return n}function an(e){let n=e.endpointId??e.agent?.endpointId??e.agent?.id;if(!n)throw Error("WebSocket run requires agent or endpointId.");return n}function ee(e,n){if(e.type!=="output")return e;let t=e;return n.onOutput?.(t),t.data}function ne(e){return Error(`Cannot access the WebSocket service at ${e}.`)}var re=Symbol.for("broods.resource"),se=Symbol.for("broods.config"),ce=Symbol.for("broods.connection"),oe=/^[A-Z][A-Z0-9_]{0,63}$/,Oe=new Proxy((e)=>{if(!oe.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"&&oe.test(n))throw Error(`env.${n} is not supported; use env("${n}")`);return Reflect.get(e,n,t)}});function Ne(e){let{name:n,description:t,...o}=e;return d("agent",n,t,o)}function je(e){return{[se]:!0,config:e}}function qe(e){let{name:n,description:t,...o}=e;return d("cron",n,t,o)}function De(e){return e}function Le(e){let{name:n,description:t,...o}=e;return d("mcp",n,t,o)}function Ue(e){let{name:n,description:t,...o}=e;return d("policy",n,t,o)}function Fe(e){let{name:n,description:t,...o}=e;return d("sandbox",n,t,o)}function Be(e){let{name:n,description:t,...o}=e;return d("skill",n,t,o)}function Ve(e){let{name:n,description:t,...o}=e;return d("workspace",n,t,o)}function Ze(e){return y("discord",e)}function He(e){return y("github",e)}function We(e){return y("pancake",e)}function Ge(e){return y("slack",e)}function Ke(e){return y("telegram",e)}function Je(e){return y("zalo",e)}function Ye(e){let{name:n,description:t,channelId:o,guildId:r,...s}=e;return p(n,t,o,r,s)}function Xe(e){let{name:n,description:t,repo:o,...r}=e,[s,...i]=o.split("/");if(!s||i.length!==1||!i[0])throw Error(`Channel "${n}" repo must be "owner/name", not "${o}"`);return p(n,t,o,s,r)}function Qe(e){let{name:n,description:t,conversationId:o,...r}=e;return p(n,t,o,void 0,r)}function et(e){let{name:n,description:t,channelId:o,teamId:r,...s}=e;return p(n,t,o,r,s)}function tt(e){let{name:n,description:t,chatId:o,...r}=e;return p(n,t,o,void 0,r)}function rt(e){let{name:n,description:t,chatId:o,...r}=e;return p(n,t,o,void 0,r)}function ie(e){return Boolean(e&&typeof e==="object"&&e[se])}function R(e){return Boolean(e&&typeof e==="object"&&e[ce])}function h(e){return Boolean(e&&typeof e==="object"&&e[re])}function p(e,n,t,o,r){return d("channelRecord",e,n,{...r,externalId:t,...o?{workspaceRef:o}:{}})}function y(e,n){let{partition:t,...o}=n;return{[ce]:!0,kind:"connection",type:e,...t?{partition:t}:{},config:o}}function d(e,n,t,o){return{[re]:!0,kind:e,name:n,...t?{description:t}:{},config:o}}
5
- export{W,Se,Me,xe,Ae,ze,G,$e,v,w,k,K,J,_,g,ae,f,N,P,we,F,Y,ke,X,I,Oe,Ne,je,qe,De,Le,Ue,Fe,Be,Ve,Ze,He,We,Ge,Ke,Je,Ye,Xe,Qe,et,tt,rt,ie,R,h};
2
+ `);o=i.pop()??"";for(let a of i)if(a.startsWith("data: "))yield a.slice(6)}}finally{n.releaseLock()}}var Me=500,ge=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/,le=/^(?!0{32}$)[0-9a-f]{32}$/;function xe(e){return e==="DEBUG"||e==="INFO"||e==="WARN"||e==="ERROR"}function Ae(e){if(typeof e!=="object"||e===null)return!1;let n=e;if(n.type==="subscribe")return de(n);if(n.type==="unsubscribe")return D(n.stream);if(n.type==="fetchTrace")return $e(n.traceId);return!1}function ze(e){return e==="task"||e==="cron"||e==="subtask"}function K(e){return typeof e==="string"&&ge.test(e)}function $e(e){return typeof e==="string"&&le.test(e)}function D(e){return e==="logs"||e==="traces"}function de(e){let n=e.stream;if(!D(n))return!1;let t=e.backfill;if(t!==void 0&&(typeof t!=="number"||!Number.isSafeInteger(t)||t<0||t>500))return!1;if(e.liveOnly!==void 0&&typeof e.liveOnly!=="boolean")return!1;let o=e.minLevel;if(o!==void 0&&!xe(o))return!1;let r=e.sandboxId;return r===void 0||n==="logs"&&K(r)}import{mkdir as pe,readFile as fe,writeFile as ye}from"node:fs/promises";import{homedir as me}from"node:os";import{dirname as be,join as Re}from"node:path";var v="broods",w="_generated",k=Re(me(),".broods","config.json"),T="broods.app",M="dashboard",he="gateway";async function G(){let e=process.env.BROODS_TOKEN,n=process.env.BROODS_BASE_URL;if(e&&n)return{baseUrl:h(n),token:e,createdAt:new Date().toISOString()};try{let t=JSON.parse(await fe(k,"utf8"));if(typeof t.baseUrl!=="string"||!t.baseUrl)return null;return t}catch{return null}}async function J(e){await pe(be(k),{recursive:!0}),await ye(k,`${JSON.stringify(e,null,2)}
3
+ `,"utf8")}function _(){return process.env.BROODS_STAGE}function h(e){let n=e.length;while(n>0&&e.charCodeAt(n-1)===47)n-=1;return e.slice(0,n)}function ae(e){try{let n=new URL(e);if(!n.hostname.startsWith(`${M}.`))return;let t=n.hostname.slice(M.length+1);if(t!==T&&!t.endsWith(`.${T}`))return;return n.hostname=`${he}.${t}`,h(n.origin)}catch{return}}import{existsSync as B,readFileSync as O,statSync as Ce}from"node:fs";import{join as H,resolve as L}from"node:path";var E=new Map,Ie=new Set(Object.keys(process.env)),U=new Set;function f(e=process.cwd()){ve(e);let n=Te();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,stage:_()}}function N(e,n=process.cwd()){let t=process.env[e];if(t===void 0)return!1;let o=L(n);for(let r of[".env.local",".env"]){let s=H(o,r);if(!B(s))continue;let i=j(O(s,"utf8"));if(i[e]!==void 0)return i[e]!==t}return!0}function ve(e){let n=L(e),t=process.env.BROODS_RELOAD_ENV==="1";for(let o of[".env",".env.local"]){let r=H(n,o);if(!B(r)){E.delete(r);continue}let s=Ce(r).mtimeMs,i=E.get(r);if(i&&i.mtimeMs===s)continue;E.set(r,{mtimeMs:s});for(let[a,u]of Object.entries(j(O(r,"utf8"))))if(t||!Ie.has(a)||U.has(a))process.env[a]=u,U.add(a)}}function j(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]=Ee(r.slice(s+1).trim())}return n}function Ee(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 Te(){try{let e=JSON.parse(O(k,"utf8"));if(typeof e.baseUrl!=="string"||typeof e.token!=="string")return null;return{baseUrl:h(e.baseUrl),...typeof e.dashboardUrl==="string"?{dashboardUrl:h(e.dashboardUrl)}:{},token:e.token}}catch{return null}}var P="https://gateway.broods.app";class we extends Error{accepted;constructor(e){super(`Ingress ${e.eventId} was accepted asynchronously with status ${e.status??"queued"}`);this.accepted=e;this.name="IngressAcceptedError"}}class F{baseUrl;apiKey;fetchImpl;constructor(e={}){f(),this.baseUrl=Y(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||P),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}/v1/webhooks/${t.join("/")}`}stageWebhookUrl(e,n,t){let o=[e,"dev",n,t].map(encodeURIComponent);return`${this.baseUrl}/v1/webhooks/${o.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}),continue:(s)=>this.continue({...s,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),continue:(o)=>this.continue(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=q(t,"cli"),r=n?this.scopedUrl(e):`${this.baseUrl}/v1/runs`,s=await this.openStream(o,r);if(!s.ok)throw Error(`Run failed: ${s.status} ${await s.text()}`);if(s.status===202)throw new we(Z(await s.json(),o));if(!s.body)throw Error("Run response has no body");for await(let i of Se(s.body)){let a;try{a=JSON.parse(i)}catch{continue}if(a.type==="error")throw Error(`Agent run failed: ${Pe(a.error)}`);yield a}}async runAsync(e,n){let t=n?{...n,agentId:e.id,agentName:e.name}:e,o={...q(t,"async"),background:!0},r=n?this.scopedUrl(e):`${this.baseUrl}/v1/runs`;return this.postAcceptedRun(r,o,"Async run")}async continue(e,n){let t=n?e:null,o=n??e,r={agentId:t?t.id:e.agentId,eventId:o.eventId??`continue-${crypto.randomUUID()}`,conversationKey:o.conversationKey,continue:!0},s=t?this.scopedUrl(t):`${this.baseUrl}/v1/runs`;return this.postAcceptedRun(s,r,"Continue")}async getAsyncStatus(e){let n=this.resolveStatusUrl(e),t=await this.fetchJson(n,{method:"GET",headers:this.apiKeyHeaders()});if(t.status===404)return{status:"not_found"};if(!t.ok)throw Error(`Status check failed: ${t.status} ${await t.text()}`);return await t.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);if(r.status==="awaiting_approval"||r.status==="awaiting_input"||r.status==="completed"||r.status==="failed"||r.status==="expired"||r.status==="not_found")return r;await tn(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(en(e))});if(n.status!==201)throw Error(`Create cron job failed: ${n.status} ${await n.text()}`);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 e.text()}`);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 n.text()}`);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 r.text()}`);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 t.text()}`);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 n.text()}`);return(await n.json()).deleted}scopedUrl(e){if(e.projectSlug&&e.stageSlug&&e.endpointId)return`${this.baseUrl}/v1/projects/${encodeURIComponent(e.projectSlug)}/stages/${encodeURIComponent(e.stageSlug)}/agents/${encodeURIComponent(e.endpointId)}`;return`${this.baseUrl}/v1/runs`}asyncAgentRun(e,n){return{...e,conversationKey:n,poll:()=>this.getAsyncStatus(e),wait:(t)=>this.waitForAsyncStatus(e,t)}}async postAcceptedRun(e,n,t){let o=await this.fetchJson(e,{method:"POST",headers:this.apiKeyHeaders(),body:JSON.stringify(n)});if(o.status!==202)throw Error(`${t} failed: ${o.status} ${await nn(o,"202 JSON")}`);return this.asyncAgentRun(Z(await o.json(),n),n.conversationKey)}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 ${P} 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){if(typeof e!=="string")return e.statusUrl;if(/^https?:\/\//.test(e))return e;return on(this.baseUrl,e)}}function Y(e){let n=e.trim(),t=/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(n)?n:`https://${n}`;return h(t)}function q(e,n){let t=e.eventId??`${n}-${Date.now()}`;return{agentId:e.agentId,eventId:t,conversationKey:e.conversationKey??t,events:W(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 Pe(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}`}function Z(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=typeof e.runId==="string"?e.runId:_e(t);if(!o)throw Error("Async response missing runId");let r=typeof e.eventId==="string"?e.eventId:o,{status:s,requestedMode:i}=e;return{statusUrl:t,runId:o,eventId:r,agentId:n.agentId,...s==="accepted"||s==="queued"||s==="applied"||s==="processing"?{status:s}:{},...i==="reject"||i==="followup"||i==="collect"||i==="steer"?{requestedMode:i}:{}}}function _e(e){let n=new URL(e).pathname.match(/\/v1\/runs\/([^/]+)$/);return n?.[1]?decodeURIComponent(n[1]):void 0}function en(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}}async function nn(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 ignored the background flag and ran the request on the direct streaming runner.`;let o=await e.text();if(o.length<=2000)return o;return`${o.slice(0,2000)}... [truncated ${o.length-2000} chars]`}function tn(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 on(e,n){return`${Y(e)}/v1/runs/${encodeURIComponent(n)}`}var rn=2000,z=0,b=1;class ke{baseUrl;apiKey;WebSocketImpl;connectTimeoutMs;constructor(e={}){if(f(),this.baseUrl=te(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||P),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??rn}subscribe(e,n={}){let t=this.resolveWebSocket(),o=this.buildUrl(e),r=sn(e),s=ne(this.baseUrl),i=new t(o,I(this.apiKey)),a=!1,u=!1,l=setTimeout(()=>{if(!a)m(s)},this.connectTimeoutMs),x=(c=1000,S="client closed")=>{if(u)return;if(u=!0,clearTimeout(l),e.signal?.removeEventListener("abort",C),i.readyState===b||i.readyState===z)i.close(c,S)},m=(c)=>{n.onError?.(c),x(1011,"websocket error")},ue=()=>{n.onDone?.(),x(1000,"done")},C=()=>{if(i.readyState===b)i.send(JSON.stringify({type:"cancel"}));m(Error("WebSocket run aborted."))};return e.signal?.addEventListener("abort",C,{once:!0}),i.onopen=()=>{if(a=!0,clearTimeout(l),e.signal?.aborted){C();return}i.send(JSON.stringify({type:"execute",agentId:r,...e.answers?{answers:e.answers}:{events:W(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 S=Q(c.data);if(!S)return;let A=ee(S,n);switch(n.onMessage?.(A),A.type){case"meta":n.onMeta?.(A);break;case"done":ue();break;case"error":m(Error(V(A.error)));break}},i.onerror=()=>{m(s)},i.onclose=(c)=>{if(clearTimeout(l),e.signal?.removeEventListener("abort",C),u||c.code===1000)return;m(Error(c.reason||"WebSocket connection closed."))},{url:o,sendControl:function(c){if(i.readyState!==b)throw Error("WebSocket is not open");i.send(JSON.stringify({type:"control",...c}))},close:x}}attach(e,n={}){let t=this.resolveWebSocket(),o=this.buildUrl(e),r=new t(o,I(this.apiKey)),s=!1,i=(a=1000,u="client closed")=>{if(s)return;if(s=!0,r.readyState===b||r.readyState===z)r.close(a,u)};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 u=Q(a.data);if(!u)return;let l=ee(u,n);if(n.onMessage?.(l),l.type==="done")n.onDone?.();if(l.type==="error")n.onError?.(Error(V(l.error)))},r.onerror=()=>n.onError?.(ne(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!==b)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=an(e),t=e.projectSlug??e.agent?.projectSlug,o=e.stageSlug??e.agent?.stageSlug,r=t&&o?`/projects/${encodeURIComponent(t)}/stages/${encodeURIComponent(o)}`:"";return`${X(this.baseUrl)}/v1${r}/agents/${encodeURIComponent(n)}/ws`}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 X(e){let n=new URL(te(e));if(n.protocol==="https:")n.protocol="wss:";if(n.protocol==="http:")n.protocol="ws:";return h(n.toString())}function I(e){return["broods.v1",`broods.token.${e}`]}function V(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)}function te(e){let n=e.trim();if(/^wss?:\/\//.test(n))return h(n);return Y(n)}function Q(e){try{let n=JSON.parse(e);return typeof n==="object"&&n!==null&&typeof n.type==="string"?n:null}catch{return null}}function sn(e){let n=e.agentId??e.agent?.id??e.endpointId;if(!n)throw Error("WebSocket run requires agent, agentId, or endpointId.");return n}function an(e){let n=e.endpointId??e.agent?.endpointId??e.agent?.id;if(!n)throw Error("WebSocket run requires agent or endpointId.");return n}function ee(e,n){if(e.type!=="output")return e;let t=e;return n.onOutput?.(t),t.data}function ne(e){return Error(`Cannot access the WebSocket service at ${e}.`)}var re=Symbol.for("broods.resource"),se=Symbol.for("broods.config"),ce=Symbol.for("broods.connection"),oe=/^[A-Z][A-Z0-9_]{0,63}$/,Oe=new Proxy((e)=>{if(!oe.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"&&oe.test(n))throw Error(`env.${n} is not supported; use env("${n}")`);return Reflect.get(e,n,t)}});function Ne(e){let{name:n,description:t,...o}=e;return d("agent",n,t,o)}function je(e){return{[se]:!0,config:e}}function qe(e){let{name:n,description:t,...o}=e;return d("cron",n,t,o)}function De(e){return e}function Le(e){let{name:n,description:t,...o}=e;return d("mcp",n,t,o)}function Ue(e){let{name:n,description:t,...o}=e;return d("policy",n,t,o)}function Fe(e){let{name:n,description:t,...o}=e;return d("sandbox",n,t,o)}function Be(e){let{name:n,description:t,...o}=e;return d("skill",n,t,o)}function Ve(e){let{name:n,description:t,...o}=e;return d("workspace",n,t,o)}function Ze(e){return y("discord",e)}function He(e){return y("github",e)}function We(e){return y("pancake",e)}function Ke(e){return y("slack",e)}function Ge(e){return y("telegram",e)}function Je(e){return y("zalo",e)}function Ye(e){let{name:n,description:t,channelId:o,guildId:r,...s}=e;return p(n,t,o,r,s)}function Xe(e){let{name:n,description:t,repo:o,...r}=e,[s,...i]=o.split("/");if(!s||i.length!==1||!i[0])throw Error(`Channel "${n}" repo must be "owner/name", not "${o}"`);return p(n,t,o,s,r)}function Qe(e){let{name:n,description:t,conversationId:o,...r}=e;return p(n,t,o,void 0,r)}function et(e){let{name:n,description:t,channelId:o,teamId:r,...s}=e;return p(n,t,o,r,s)}function tt(e){let{name:n,description:t,chatId:o,...r}=e;return p(n,t,o,void 0,r)}function rt(e){let{name:n,description:t,chatId:o,...r}=e;return p(n,t,o,void 0,r)}function ie(e){return Boolean(e&&typeof e==="object"&&e[se])}function R(e){return Boolean(e&&typeof e==="object"&&e[ce])}function g(e){return Boolean(e&&typeof e==="object"&&e[re])}function p(e,n,t,o,r){return d("channelRecord",e,n,{...r,externalId:t,...o?{workspaceRef:o}:{}})}function y(e,n){let{partition:t,...o}=n;return{[ce]:!0,kind:"connection",type:e,...t?{partition:t}:{},config:o}}function d(e,n,t,o){return{[re]:!0,kind:e,name:n,...t?{description:t}:{},config:o}}
5
+ export{W,Se,Me,xe,Ae,ze,K,$e,v,w,k,G,J,_,h,ae,f,N,P,we,F,Y,ke,X,I,Oe,Ne,je,qe,De,Le,Ue,Fe,Be,Ve,Ze,He,We,Ke,Ge,Je,Ye,Xe,Qe,et,tt,rt,ie,R,g};