broods 0.34.1 → 0.36.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/index.d.ts CHANGED
@@ -130,6 +130,7 @@ interface AgentConfig {
130
130
  model?: AgentModelConfig;
131
131
  provider?: AgentProviderConfig;
132
132
  sandbox?: string;
133
+ sandboxes?: string[];
133
134
  workspaces?: AgentWorkspaceRef[];
134
135
  session?: AgentSessionConfig;
135
136
  hooks?: AgentHooksConfig;
@@ -475,7 +476,7 @@ type UpdateCronInput = {
475
476
  * storage/sandbox-config.ts normalizer so the public /v1/sandboxes contract
476
477
  * is unchanged. The public projection lives in ./responses.ts.
477
478
  */
478
- declare const SANDBOX_PROVIDERS: readonly ["sandbox", "lambda", "e2b", "daytona", "vercel"];
479
+ declare const SANDBOX_PROVIDERS: readonly ["sandbox", "lambda", "e2b", "daytona", "vercel", "machine"];
479
480
  declare const SANDBOX_RUNTIMES: readonly ["bash", "python", "node"];
480
481
  declare const SANDBOX_PERMISSION_MODES: readonly ["edit", "ask", "bypass"];
481
482
  declare const SANDBOX_NETWORK_MODES: readonly ["allow-all", "deny-all", "restricted"];
@@ -763,10 +764,12 @@ type AgentRunEventInput = {
763
764
  /** Shorthand for a single user text message. */
764
765
  input: string;
765
766
  events?: never;
767
+ answers?: never;
766
768
  } | {
767
769
  /** Full-fidelity event list for multimodal content or tool responses. */
768
770
  events: [ModelMessage, ...ModelMessage[]];
769
771
  input?: never;
772
+ answers?: never;
770
773
  };
771
774
  /**
772
775
  * Resolves a run's events from either the explicit `events` list or the `input`
@@ -808,6 +811,17 @@ interface PendingQuestion {
808
811
  allowFreeText?: boolean;
809
812
  }[];
810
813
  }
814
+ /** Answers one open prompt: option labels or free text, keyed by question id. */
815
+ interface QuestionAnswer {
816
+ statusId: string;
817
+ answers: Record<string, string[]>;
818
+ }
819
+ /** Settles open prompts instead of sending events; the run resumes on this socket. */
820
+ type AgentRunAnswerInput = {
821
+ answers: [QuestionAnswer, ...QuestionAnswer[]];
822
+ input?: never;
823
+ events?: never;
824
+ };
811
825
  type WebSocketStreamMessage = AgentStreamPart | {
812
826
  type: "question-request";
813
827
  questions: PendingQuestion[];
@@ -870,7 +884,7 @@ type WebSocketClientExecuteMessage = {
870
884
  /** Defaults to "steer": join the live run at its next step boundary. */
871
885
  mode?: IngressMode;
872
886
  idempotencyKey?: string;
873
- } & AgentRunEventInput & AgentRunOverrides;
887
+ } & (AgentRunEventInput | AgentRunAnswerInput) & AgentRunOverrides;
874
888
  type WebSocketClientControlMessage = {
875
889
  type: "control";
876
890
  requestId: string;
@@ -1782,7 +1796,7 @@ type WebSocketRunInput = {
1782
1796
  /** Defaults to "steer": join the live run at its next step boundary. */
1783
1797
  mode?: "reject" | "followup" | "collect" | "steer";
1784
1798
  idempotencyKey?: string;
1785
- } & AgentRunEventInput & AgentRunOverrides;
1799
+ } & (AgentRunEventInput | AgentRunAnswerInput) & AgentRunOverrides;
1786
1800
  type WebSocketAttachInput = Omit<WebSocketClientAttachMessage, "type"> & {
1787
1801
  endpointId?: string;
1788
1802
  agent?: AgentReference;
@@ -1953,15 +1967,21 @@ type McpHandler = ((request: Request) => Response | Promise<Response>) | {
1953
1967
  fetch(request: Request): Response | Promise<Response>;
1954
1968
  };
1955
1969
  /**
1956
- * MCP server registration (#331), either external (`url`) or hosted (`handler`).
1957
- * Either way the server's tools are offered as `<name>__<tool>`; an external
1958
- * row is dialed over the stateless HTTP transport (spec 2026-07-28) at agent
1959
- * registration time. The name namespaces those tools, so it must be 1-32
1960
- * lowercase letters, digits, or hyphens, starting with a letter.
1970
+ * MCP server registration (#331): external (`url`), hosted (`handler`), or on
1971
+ * a user's computer (`sandbox`). Either way the server's tools are offered as
1972
+ * `<name>__<tool>`; an external row is dialed over the stateless HTTP
1973
+ * transport (spec 2026-07-28) at agent registration time. The name namespaces
1974
+ * those tools, so it must be 1-32 lowercase letters, digits, or hyphens,
1975
+ * starting with a letter.
1961
1976
  */
1962
1977
  interface McpDefinitionConfig {
1963
1978
  /** External server's MCP endpoint; http(s), no embedded credentials. */
1964
1979
  url?: string;
1980
+ /**
1981
+ * Instead of `url` or `handler`: the machine sandbox whose daemon runs this
1982
+ * server, from the entry with the same name in its `--mcp` file.
1983
+ */
1984
+ sandbox?: SandboxResource | string;
1965
1985
  /**
1966
1986
  * Hosted alternative to `url`: declare the server inline as
1967
1987
  * `handler: createMcpHandler(...)` from @modelcontextprotocol/server,
@@ -2302,6 +2322,11 @@ type AgentDefinitionConfig = EnvRefString<Pick<AgentConfig, "agent" | "model" |
2302
2322
  };
2303
2323
  connections?: readonly AnyConnectionDefinition[];
2304
2324
  sandbox?: SandboxResource | string;
2325
+ /**
2326
+ * Extra sandboxes bash picks by name with no workspace mounted, each with its
2327
+ * own image, size, network and permissionMode. Never repeat `sandbox` here.
2328
+ */
2329
+ sandboxes?: readonly (SandboxResource | string)[];
2305
2330
  workspaces?: readonly AgentWorkspaceInput[];
2306
2331
  subagent?: AgentSubagentDefinitionConfig;
2307
2332
  skills?: AgentSkillsDefinitionConfig;
@@ -2386,4 +2411,4 @@ declare function isConnectionDefinition(value: unknown): value is AnyConnectionD
2386
2411
  declare function isResource(value: unknown): value is AnyResource;
2387
2412
 
2388
2413
  export { BroodsAccountApiError, BroodsAccountClient, BroodsClient, BroodsWebSocketClient, DEFAULT_CORE_BASE_URL, IngressAcceptedError, MAX_OBSERVABILITY_BACKFILL, BroodsWebSocketClient as WebSocketClient, BroodsWebSocketClient as WebsocketClient, defineAgent, defineBroods, defineCron, defineDiscordChannel, defineDiscordConnection, defineGitHubChannel, defineGitHubConnection, defineHarness, defineMcp, definePancakeChannel, definePancakeConnection, definePolicy, defineSandbox, defineSkill, defineSlackChannel, defineSlackConnection, defineTelegramChannel, defineTelegramConnection, defineWorkspace, defineZaloChannel, defineZaloConnection, env, envPlaceholder, isBroodsConfig, isConnectionDefinition, isLogLevel, isObservabilityClientMessage, isResource, isRootSpanKind, isSandboxLogId, isTraceId, normalizeHttpServiceUrl, readSseStream, resolveEnvCredential, resolveRunEvents, toWebSocketBaseUrl, webSocketSubprotocols };
2389
- export type { Account, AccountAgent, AccountChannel, AccountEnvVar, AccountMcp, AccountPolicy, AccountRole, AccountSandbox, AccountWorkspace, Agent, AgentChannelsConfig, AgentCodeHookConfig, AgentConfig, AgentConfigDoc, AgentContinueInput, AgentDefinitionConfig, AgentDiscordChannelConfig, AgentGitHubChannelConfig, AgentHandle, AgentHookEventName, AgentHooks, AgentHooksConfig, AgentPancakeChannelConfig, AgentProviderSettings, AgentReference, AgentResource, AgentRunEventInput, AgentRunInput, AgentRunModelOverrides, AgentRunOverrides, AgentRunResult, AgentSkillsDefinitionConfig, AgentSlackChannelConfig, AgentStreamPart, AgentSubagentDefinitionConfig, AgentTelegramChannelConfig, AgentWebhookHookConfig, AgentWorkspaceInput, AgentWorkspaceRef, AgentWorkspaceRefInput, AgentZaloChannelConfig, AnyConnectionDefinition, AnyResource, AssumeRoleResult, AsyncAgentRun, AsyncPollOptions, AsyncRequestAccepted, AsyncStatus, BroodsAccount, BroodsAccountClientOptions, BroodsClientOptions, BroodsConfigDefinition, BroodsProjectConfig, BroodsWebSocketClientOptions, ChannelAgentInput, ChannelDefinitionConfig, ChannelMessageReceived, ChannelPartition, ChannelRecordConfig, ChannelReference, ChannelReplyIn, ChannelResource, ChannelType, CliManifest, CliManifestResource, ConnectionDefinition, CreateAgentResult, CreateClientCronInput, CreateCronInput, CreateMcpInput, Cron, CronDefinitionConfig, CronDoc, CronLastStatus, CronResource, CronRun, CronStatus, DeleteAccountResult, DiscordChannelInput, DiscordConnectionDefinition, DiscordConnectionInput, DiscordMessageSource, DiscordSource, Doc, EnvAccessor, EnvRef, EnvRefString, GeneratedIds, GitHubChannelInput, GitHubConnectionDefinition, GitHubConnectionInput, GitHubMessageSource, GitHubSource, HarnessDefinition, HarnessType, HookContext, Id, IngressMode, IngressStatus, LogLevel, McpDefinitionConfig, McpHandler, McpOauthInput, McpResource, ObservabilityBackfillMessage, ObservabilityClientMessage, ObservabilityErrorMessage, ObservabilityFetchTraceMessage, ObservabilityLogEntry, ObservabilityLogMessage, ObservabilityReadyMessage, ObservabilityServerMessage, ObservabilitySpanMessage, ObservabilitySpanRow, ObservabilitySubscribeMessage, ObservabilityUnsubscribeMessage, PancakeChannelInput, PancakeConnectionDefinition, PancakeConnectionInput, PancakeMessageSource, PancakeSource, PendingQuestion, PolicyDefinitionConfig, PolicyDocument, PolicyResource, ProjectDoc, ProviderConfigInput, ProviderSettingsInput, ResourceApi, ResourceDefinition, ResourceInput, ResourceKind, RotateSecretResult, Sandbox, SandboxConfig, SandboxConfigDoc, SandboxDefinitionConfig, SandboxDefinitionOptions, SandboxLifecycleResult, SandboxResource, SandboxSnapshotResult, SandboxTerminalTicket, Skill, SkillDefinitionConfig, SkillResource, SkillUploadInput, SlackChannelInput, SlackConnectionDefinition, SlackConnectionInput, SlackMessageSource, SlackSource, StageDoc, StageScope, TelegramChannelInput, TelegramConnectionDefinition, TelegramConnectionInput, TelegramMessageSource, TelegramSource, ToolApprovalSummary, UpdateAgentInput, UpdateCronInput, UpdateMcpInput, WebSocketAttachInput, WebSocketClientAttachMessage, WebSocketClientCancelMessage, WebSocketClientControlMessage, WebSocketClientExecuteMessage, WebSocketClientMessage, WebSocketConstructorLike, WebSocketHandlers, WebSocketLike, WebSocketOutputMessage, WebSocketRunInput, WebSocketServerMessage, WebSocketStreamMessage, WebSocketSubscription, Workspace, WorkspaceConfig, WorkspaceConfigDoc, WorkspaceDefinitionConfig, WorkspaceFileEntry, WorkspaceResource, ZaloChannelInput, ZaloConnectionDefinition, ZaloConnectionInput, ZaloMessageSource, ZaloSource };
2414
+ export type { Account, AccountAgent, AccountChannel, AccountEnvVar, AccountMcp, AccountPolicy, AccountRole, AccountSandbox, AccountWorkspace, Agent, AgentChannelsConfig, AgentCodeHookConfig, AgentConfig, AgentConfigDoc, AgentContinueInput, AgentDefinitionConfig, AgentDiscordChannelConfig, AgentGitHubChannelConfig, AgentHandle, AgentHookEventName, AgentHooks, AgentHooksConfig, AgentPancakeChannelConfig, AgentProviderSettings, AgentReference, AgentResource, AgentRunAnswerInput, AgentRunEventInput, AgentRunInput, AgentRunModelOverrides, AgentRunOverrides, AgentRunResult, AgentSkillsDefinitionConfig, AgentSlackChannelConfig, AgentStreamPart, AgentSubagentDefinitionConfig, AgentTelegramChannelConfig, AgentWebhookHookConfig, AgentWorkspaceInput, AgentWorkspaceRef, AgentWorkspaceRefInput, AgentZaloChannelConfig, AnyConnectionDefinition, AnyResource, AssumeRoleResult, AsyncAgentRun, AsyncPollOptions, AsyncRequestAccepted, AsyncStatus, BroodsAccount, BroodsAccountClientOptions, BroodsClientOptions, BroodsConfigDefinition, BroodsProjectConfig, BroodsWebSocketClientOptions, ChannelAgentInput, ChannelDefinitionConfig, ChannelMessageReceived, ChannelPartition, ChannelRecordConfig, ChannelReference, ChannelReplyIn, ChannelResource, ChannelType, CliManifest, CliManifestResource, ConnectionDefinition, CreateAgentResult, CreateClientCronInput, CreateCronInput, CreateMcpInput, Cron, CronDefinitionConfig, CronDoc, CronLastStatus, CronResource, CronRun, CronStatus, DeleteAccountResult, DiscordChannelInput, DiscordConnectionDefinition, DiscordConnectionInput, DiscordMessageSource, DiscordSource, Doc, EnvAccessor, EnvRef, EnvRefString, GeneratedIds, GitHubChannelInput, GitHubConnectionDefinition, GitHubConnectionInput, GitHubMessageSource, GitHubSource, HarnessDefinition, HarnessType, HookContext, Id, IngressMode, IngressStatus, LogLevel, McpDefinitionConfig, McpHandler, McpOauthInput, McpResource, ObservabilityBackfillMessage, ObservabilityClientMessage, ObservabilityErrorMessage, ObservabilityFetchTraceMessage, ObservabilityLogEntry, ObservabilityLogMessage, ObservabilityReadyMessage, ObservabilityServerMessage, ObservabilitySpanMessage, ObservabilitySpanRow, ObservabilitySubscribeMessage, ObservabilityUnsubscribeMessage, PancakeChannelInput, PancakeConnectionDefinition, PancakeConnectionInput, PancakeMessageSource, PancakeSource, PendingQuestion, PolicyDefinitionConfig, PolicyDocument, PolicyResource, ProjectDoc, ProviderConfigInput, ProviderSettingsInput, QuestionAnswer, ResourceApi, ResourceDefinition, ResourceInput, ResourceKind, RotateSecretResult, Sandbox, SandboxConfig, SandboxConfigDoc, SandboxDefinitionConfig, SandboxDefinitionOptions, SandboxLifecycleResult, SandboxResource, SandboxSnapshotResult, SandboxTerminalTicket, Skill, SkillDefinitionConfig, SkillResource, SkillUploadInput, SlackChannelInput, SlackConnectionDefinition, SlackConnectionInput, SlackMessageSource, SlackSource, StageDoc, StageScope, TelegramChannelInput, TelegramConnectionDefinition, TelegramConnectionInput, TelegramMessageSource, TelegramSource, ToolApprovalSummary, UpdateAgentInput, UpdateCronInput, UpdateMcpInput, WebSocketAttachInput, WebSocketClientAttachMessage, WebSocketClientCancelMessage, WebSocketClientControlMessage, WebSocketClientExecuteMessage, WebSocketClientMessage, WebSocketConstructorLike, WebSocketHandlers, WebSocketLike, WebSocketOutputMessage, WebSocketRunInput, WebSocketServerMessage, WebSocketStreamMessage, WebSocketSubscription, Workspace, WorkspaceConfig, WorkspaceConfigDoc, WorkspaceDefinitionConfig, WorkspaceFileEntry, WorkspaceResource, ZaloChannelInput, ZaloConnectionDefinition, ZaloConnectionInput, ZaloMessageSource, ZaloSource };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{B,pe,xe,Te,ke,Pe,V,Ie,C,me,q,W,Ce,K,D,Ae,Me,ze,Oe,Ne,je,qe,De,Le,Ue,Fe,Be,Ve,Ze,He,We,Ke,Ge,Je,Ye,Xe,Qe,ne,_,h}from"./chunk-wgwmcq6q.js";import{m,$e,L,U}from"./chunk-yd3vh7k7.js";export{m as BroodsAccountApiError,U as BroodsAccountClient,q as BroodsClient,Ce as BroodsWebSocketClient,C as DEFAULT_CORE_BASE_URL,me as IngressAcceptedError,xe as MAX_OBSERVABILITY_BACKFILL,Ce as WebSocketClient,Ce as WebsocketClient,Me as defineAgent,ze as defineBroods,Oe as defineCron,Ke as defineDiscordChannel,Fe as defineDiscordConnection,Ge as defineGitHubChannel,Be as defineGitHubConnection,Ne as defineHarness,je as defineMcp,Je as definePancakeChannel,Ve as definePancakeConnection,qe as definePolicy,De as defineSandbox,Le as defineSkill,Ye as defineSlackChannel,Ze as defineSlackConnection,Xe as defineTelegramChannel,He as defineTelegramConnection,Ue as defineWorkspace,Qe as defineZaloChannel,We as defineZaloConnection,Ae as env,$e as envPlaceholder,ne as isBroodsConfig,_ as isConnectionDefinition,Te as isLogLevel,ke as isObservabilityClientMessage,h as isResource,Pe as isRootSpanKind,V as isSandboxLogId,Ie as isTraceId,W as normalizeHttpServiceUrl,pe as readSseStream,L as resolveEnvCredential,B as resolveRunEvents,K as toWebSocketBaseUrl,D as webSocketSubprotocols};
1
+ import{W,Se,Me,xe,Ae,ze,G,$e,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}from"./chunk-q2n2mxeg.js";import{m,Ce,L,U}from"./chunk-mqsvqhkj.js";export{m as BroodsAccountApiError,U as BroodsAccountClient,F as BroodsClient,ke as BroodsWebSocketClient,P as DEFAULT_CORE_BASE_URL,we as IngressAcceptedError,Me as MAX_OBSERVABILITY_BACKFILL,ke as WebSocketClient,ke as WebsocketClient,Ne as defineAgent,je as defineBroods,qe as defineCron,Ye as defineDiscordChannel,Ze as defineDiscordConnection,Xe as defineGitHubChannel,He as defineGitHubConnection,De as defineHarness,Le as defineMcp,Qe as definePancakeChannel,We as definePancakeConnection,Ue as definePolicy,Fe as defineSandbox,Be as defineSkill,et as defineSlackChannel,Ge as defineSlackConnection,tt as defineTelegramChannel,Ke as defineTelegramConnection,Ve as defineWorkspace,rt as defineZaloChannel,Je as defineZaloConnection,Oe as env,Ce as envPlaceholder,ie as isBroodsConfig,R as isConnectionDefinition,xe as isLogLevel,Ae as isObservabilityClientMessage,h as isResource,ze as isRootSpanKind,G as isSandboxLogId,$e as isTraceId,Y as normalizeHttpServiceUrl,Se as readSseStream,L as resolveEnvCredential,W as resolveRunEvents,X as toWebSocketBaseUrl,I as webSocketSubprotocols};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "broods",
3
- "version": "0.34.1",
3
+ "version": "0.36.0",
4
4
  "description": "CLI + TypeScript SDK for the broods agent platform.",
5
5
  "keywords": [
6
6
  "agents",
@@ -54,6 +54,7 @@
54
54
  "test": "bun test"
55
55
  },
56
56
  "dependencies": {
57
+ "@modelcontextprotocol/client": "^2.0.0",
57
58
  "esbuild": "^0.28.2"
58
59
  },
59
60
  "devDependencies": {
@@ -63,7 +64,6 @@
63
64
  "@chat-adapter/github": "^4.38.1",
64
65
  "@chat-adapter/slack": "^4.38.1",
65
66
  "@chat-adapter/telegram": "^4.38.1",
66
- "@modelcontextprotocol/client": "^2.0.0",
67
67
  "@modelcontextprotocol/server": "2.0.0",
68
68
  "@types/bun": "^1.3.14",
69
69
  "@typescript/typescript6": "6.0.2",
@@ -1 +0,0 @@
1
- import{N}from"./chunk-xg98kc3q.js";import{L,U}from"./chunk-yd3vh7k7.js";import{e,o,i,l,s}from"./cli/index.js";var R={project:e().optional(),stage:e().optional()},M={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.",A=[{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 qt(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 N({name:"broods",version:"1"},{capabilities:{tools:{}}});if(r){for(let a of A)x(n,r,a,t.scope??{});j(n,r)}if(t.cli)O(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 P(await t())}catch(r){return k(r)}}function k(t){return{content:[{type:"text",text:t instanceof Error?t.message:String(t)}],isError:!0}}function P(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 O(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:M},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 x(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{qt as createBroodsMcpServer};
@@ -1 +0,0 @@
1
- import{x,a,F,O,G,J,Y,b,k,X,oe,se,fe,he,ge,ve,ae,ie,ye,be,Se,N}from"./chunk-xg98kc3q.js";var W=class{_readBuffer;_started=!1;_closed=!1;constructor(r=ae.stdin,o=ae.stdout,c){this._stdin=r,this._stdout=o,this._readBuffer=new ge({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=ve(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)})}},j=3000,z=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(X(r)||oe(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(k(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 A(r){let o=r.params;if(r.method==="initialize"&&!se(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(!G(o))return{kind:"legacy",reason:"no-claim"};let c=O(o),l=(c===void 0?[]:Y(c))[0];if(l!==void 0)return{kind:"invalid-envelope",issue:l};let t=J(o);if(t===void 0||!x.includes(t))return{kind:"unsupported-revision",requested:t??"unknown"};return{kind:"modern",revision:t,classification:{era:"modern",revision:t}}}function H(r,o={}){let c=o.legacy??"serve",l=o.transport??new W,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??ie),D=(e)=>{if(!k(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=O(e.params),i=G(e.params)?(n===void 0?[]:Y(n))[0]:{key:"_meta",problem:"the per-request envelope is required on protocol revision 2026-07-28"},s=J(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||!x.includes(s)){let v=new F({supported:[...x],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(k(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=fe({kind:"legacy",reason:n,...i!==void 0&&{requestedVersion:i}},x);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)},M=(e)=>{if(p||e===f)return;C()},m=async(e,n)=>{let i=await r({era:e}),s=i instanceof N?i.server:i;if(e==="modern")he(s,n),be(s,x),w.setServerCapabilities(s.getCapabilities(),Se(s));let u=new z(l,()=>M(u),e==="modern"?D:void 0);return await i.connect(u),{product:i,channel:u}},R=(e)=>e.product.close().catch((n)=>d(h(n))),V=async(e)=>{f=e.channel;try{if(!await e.channel.whenRequestsAnswered(j))d(Error(`Discarded the probe instance with requests still unanswered after ${j}ms; continuing with the fallback`));await e.product.close()}catch(n){d(h(n))}finally{f=void 0}},T=async(e)=>{if(t.phase==="closed")return;if(t.phase==="pinned"){if(t.era==="modern"&&b(e)&&e.method==="initialize"&&!se(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)&&!k(e)){d(Error("Discarded a JSON-RPC response received before the connection negotiated an era"));return}let n=A(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 F({supported:[...x],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(k(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 V(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,B=async()=>{if(E)return;E=!0;try{while(S.length>0){let e=S.shift();try{await T(e)}catch(n){if(b(e))await y(e.id,a.InternalError,"Internal server error");d(h(n))}}}finally{E=!1}},C=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),B()},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 P=l.start().catch((e)=>{throw d(h(e)),e});return P.catch(()=>{}),{close:async()=>{await P.catch(()=>{}),await C()}}}function h(r){return r instanceof Error?r:Error(String(r))}export{H as serveStdio};
@@ -1,5 +0,0 @@
1
- function B(e){if(e.events&&e.input!==void 0)throw Error("Run input accepts either `input` or `events`, not both");if(e.events&&e.events.length>0)return e.events;if(typeof e.input==="string")return[{role:"user",content:[{type:"text",text:e.input}]}];throw Error("Run input requires `input` (string) or a non-empty `events` array")}async function*pe(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 xe=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 Te(e){return e==="DEBUG"||e==="INFO"||e==="WARN"||e==="ERROR"}function ke(e){if(typeof e!=="object"||e===null)return!1;let n=e;if(n.type==="subscribe")return de(n);if(n.type==="unsubscribe")return N(n.stream);if(n.type==="fetchTrace")return Ie(n.traceId);return!1}function Pe(e){return e==="task"||e==="cron"||e==="subtask"}function V(e){return typeof e==="string"&&ge.test(e)}function Ie(e){return typeof e==="string"&&le.test(e)}function N(e){return e==="logs"||e==="traces"}function de(e){let n=e.stream;if(!N(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&&!Te(o))return!1;let r=e.sandboxId;return r===void 0||n==="logs"&&V(r)}import{mkdir as fe,readFile as ye,writeFile as be}from"node:fs/promises";import{homedir as he}from"node:os";import{dirname as Re,join as Se}from"node:path";var y="broods",S="_generated",I=Se(he(),".broods","config.json"),T="broods.app",M="dashboard",ve="gateway";async function Z(){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 ye(I,"utf8"));if(typeof t.baseUrl!=="string"||!t.baseUrl)return null;return t}catch{return null}}async function H(e){await fe(Re(I),{recursive:!0}),await be(I,`${JSON.stringify(e,null,2)}
3
- `,"utf8")}function w(){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 re(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=`${ve}.${t}`,g(n.origin)}catch{return}}import{existsSync as P,readFileSync as O,statSync as we}from"node:fs";import{join as L,resolve as j}from"node:path";var E=new Map,Ee=new Set(Object.keys(process.env)),U=new Set;function f(e=process.cwd()){$e(e);let n=en();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:w()}}function z(e,n=process.cwd()){let t=process.env[e];if(t===void 0)return!1;let o=j(n);for(let r of[".env.local",".env"]){let s=L(o,r);if(!P(s))continue;let i=G(O(s,"utf8"));if(i[e]!==void 0)return i[e]!==t}return!0}function $e(e){let n=j(e),t=process.env.BROODS_RELOAD_ENV==="1";for(let o of[".env",".env.local"]){let r=L(n,o);if(!P(r)){E.delete(r);continue}let s=we(r).mtimeMs,i=E.get(r);if(i&&i.mtimeMs===s)continue;E.set(r,{mtimeMs:s});for(let[a,u]of Object.entries(G(O(r,"utf8"))))if(t||!Ee.has(a)||U.has(a))process.env[a]=u,U.add(a)}}function G(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]=_e(r.slice(s+1).trim())}return n}function _e(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 en(){try{let e=JSON.parse(O(I,"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 C="https://gateway.broods.app";class me extends Error{accepted;constructor(e){super(`Ingress ${e.eventId} was accepted asynchronously with status ${e.status??"queued"}`);this.accepted=e;this.name="IngressAcceptedError"}}class q{baseUrl;apiKey;fetchImpl;constructor(e={}){f(),this.baseUrl=W(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||C),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=F(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 me(J(await s.json(),o));if(!s.body)throw Error("Run response has no body");for await(let i of pe(s.body)){let a;try{a=JSON.parse(i)}catch{continue}if(a.type==="error")throw Error(`Agent run failed: ${nn(a.error)}`);yield a}}async runAsync(e,n){let t=n?{...n,agentId:e.id,agentName:e.name}:e,o={...F(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 sn(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(on(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 rn(o,"202 JSON")}`);return this.asyncAgentRun(J(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 ${C} 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 an(this.baseUrl,e)}}function W(e){let n=e.trim(),t=/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(n)?n:`https://${n}`;return g(t)}function F(e,n){let t=e.eventId??`${n}-${Date.now()}`;return{agentId:e.agentId,eventId:t,conversationKey:e.conversationKey??t,events:B(e),...e.mode!==void 0?{mode:e.mode}:{},...e.idempotencyKey!==void 0?{idempotencyKey:e.idempotencyKey}:{},...e.system!==void 0?{system:e.system}:{},...e.model!==void 0?{model:e.model}:{}}}function nn(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 J(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:tn(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 tn(e){let n=new URL(e).pathname.match(/\/v1\/runs\/([^/]+)$/);return n?.[1]?decodeURIComponent(n[1]):void 0}function on(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 rn(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 sn(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 an(e,n){return`${W(e)}/v1/runs/${encodeURIComponent(n)}`}var cn=2000,Y=0,R=1;class Ce{baseUrl;apiKey;WebSocketImpl;connectTimeoutMs;constructor(e={}){if(f(),this.baseUrl=oe(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||C),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??cn}subscribe(e,n={}){let t=this.resolveWebSocket(),o=this.buildUrl(e),r=un(e),s=te(this.baseUrl),i=new t(o,D(this.apiKey)),a=!1,u=!1,l=setTimeout(()=>{if(!a)b(s)},this.connectTimeoutMs),x=(c=1000,A="client closed")=>{if(u)return;if(u=!0,clearTimeout(l),e.signal?.removeEventListener("abort",k),i.readyState===R||i.readyState===Y)i.close(c,A)},b=(c)=>{n.onError?.(c),x(1011,"websocket error")},ue=()=>{n.onDone?.(),x(1000,"done")},k=()=>{if(i.readyState===R)i.send(JSON.stringify({type:"cancel"}));b(Error("WebSocket run aborted."))};return e.signal?.addEventListener("abort",k,{once:!0}),i.onopen=()=>{if(a=!0,clearTimeout(l),e.signal?.aborted){k();return}i.send(JSON.stringify({type:"execute",agentId:r,events:B(e),sessionId:e.sessionId,...e.eventId!==void 0?{eventId:e.eventId}:{},...e.mode!==void 0?{mode:e.mode}:{},...e.idempotencyKey!==void 0?{idempotencyKey:e.idempotencyKey}:{},...e.system!==void 0?{system:e.system}:{},...e.model!==void 0?{model:e.model}:{}}))},i.onmessage=(c)=>{if(typeof c.data!=="string")return;let A=Q(c.data);if(!A)return;let v=ee(A,n);switch(n.onMessage?.(v),v.type){case"meta":n.onMeta?.(v);break;case"done":ue();break;case"error":b(Error(X(v.error)));break}},i.onerror=()=>{b(s)},i.onclose=(c)=>{if(clearTimeout(l),e.signal?.removeEventListener("abort",k),u||c.code===1000)return;b(Error(c.reason||"WebSocket connection closed."))},{url:o,sendControl:function(c){if(i.readyState!==R)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,D(this.apiKey)),s=!1,i=(a=1000,u="client closed")=>{if(s)return;if(s=!0,r.readyState===R||r.readyState===Y)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(X(l.error)))},r.onerror=()=>n.onError?.(te(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!==R)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=gn(e),t=e.projectSlug??e.agent?.projectSlug,o=e.stageSlug??e.agent?.stageSlug,r=t&&o?`/projects/${encodeURIComponent(t)}/stages/${encodeURIComponent(o)}`:"";return`${K(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 K(e){let n=new URL(oe(e));if(n.protocol==="https:")n.protocol="wss:";if(n.protocol==="http:")n.protocol="ws:";return g(n.toString())}function D(e){return["broods.v1",`broods.token.${e}`]}function X(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 oe(e){let n=e.trim();if(/^wss?:\/\//.test(n))return g(n);return W(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 un(e){let n=e.agentId??e.agent?.id??e.endpointId;if(!n)throw Error("WebSocket run requires agent, agentId, or endpointId.");return n}function gn(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 te(e){return Error(`Cannot access the WebSocket service at ${e}.`)}var ie=Symbol.for("broods.resource"),ae=Symbol.for("broods.config"),ce=Symbol.for("broods.connection"),se=/^[A-Z][A-Z0-9_]{0,63}$/,Ae=new Proxy((e)=>{if(!se.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"&&se.test(n))throw Error(`env.${n} is not supported; use env("${n}")`);return Reflect.get(e,n,t)}});function Me(e){let{name:n,description:t,...o}=e;return d("agent",n,t,o)}function ze(e){return{[ae]:!0,config:e}}function Oe(e){let{name:n,description:t,...o}=e;return d("cron",n,t,o)}function Ne(e){return e}function je(e){let{name:n,description:t,...o}=e;return d("mcp",n,t,o)}function qe(e){let{name:n,description:t,...o}=e;return d("policy",n,t,o)}function De(e){let{name:n,description:t,...o}=e;return d("sandbox",n,t,o)}function Le(e){let{name:n,description:t,...o}=e;return d("skill",n,t,o)}function Ue(e){let{name:n,description:t,...o}=e;return d("workspace",n,t,o)}function Fe(e){return m("discord",e)}function Be(e){return m("github",e)}function Ve(e){return m("pancake",e)}function Ze(e){return m("slack",e)}function He(e){return m("telegram",e)}function We(e){return m("zalo",e)}function Ke(e){let{name:n,description:t,channelId:o,guildId:r,...s}=e;return p(n,t,o,r,s)}function Ge(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 Je(e){let{name:n,description:t,conversationId:o,...r}=e;return p(n,t,o,void 0,r)}function Ye(e){let{name:n,description:t,channelId:o,teamId:r,...s}=e;return p(n,t,o,r,s)}function Xe(e){let{name:n,description:t,chatId:o,...r}=e;return p(n,t,o,void 0,r)}function Qe(e){let{name:n,description:t,chatId:o,...r}=e;return p(n,t,o,void 0,r)}function ne(e){return Boolean(e&&typeof e==="object"&&e[ae])}function _(e){return Boolean(e&&typeof e==="object"&&e[ce])}function h(e){return Boolean(e&&typeof e==="object"&&e[ie])}function p(e,n,t,o,r){return d("channelRecord",e,n,{...r,externalId:t,...o?{workspaceRef:o}:{}})}function m(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{[ie]:!0,kind:e,name:n,...t?{description:t}:{},config:o}}
5
- export{B,pe,xe,Te,ke,Pe,V,Ie,y,S,I,Z,H,w,g,re,f,z,C,me,q,W,Ce,K,D,Ae,Me,ze,Oe,Ne,je,qe,De,Le,Ue,Fe,Be,Ve,Ze,He,We,Ke,Ge,Je,Ye,Xe,Qe,ne,_,h};