broods 0.25.0 → 0.27.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
@@ -1,5 +1,12 @@
1
1
  import { SystemModelMessage, LanguageModelCallOptions, RequestOptions, streamText, JSONSchema7, ModelMessage } from 'ai';
2
2
 
3
+ /**
4
+ * Account-owned code hook upload validation for Convex config-plane sync.
5
+ * Mirrors core's account hook upload contract without requiring the Node runtime.
6
+ */
7
+ declare const AGENT_HOOK_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", "channel.message.received", "channel.message.sending"];
8
+ type AgentHookEventName = (typeof AGENT_HOOK_EVENT_NAMES)[number];
9
+
3
10
  /**
4
11
  * Every Vercel AI SDK provider that ships language models, plus `custom`
5
12
  * (any OpenAI-compatible endpoint) and `minimax`. Image-, speech- and
@@ -114,6 +121,8 @@ interface AgentConfig {
114
121
  hooks?: AgentHooksConfig;
115
122
  channels?: AgentChannelsConfig;
116
123
  tools?: AgentToolsConfig;
124
+ /** Connected MCP servers, keyed by their config-plane row id (#331). */
125
+ mcpServers?: AgentMcpServersConfig;
117
126
  /**
118
127
  * Tool names withheld for this run, applied after the tool set is built.
119
128
  * Set by a channel record; a channel can take a tool away, never add one.
@@ -269,8 +278,7 @@ interface AgentWebhookHookConfig {
269
278
  [key: string]: unknown;
270
279
  }
271
280
  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";
272
- type AgentChannelHookEventName = "channel.message.received" | "channel.message.sending";
273
- type AgentHookEventName = AgentLifecycleEventName | AgentChannelHookEventName;
281
+
274
282
  type AgentToolsConfig = Record<string, AgentToolConfig>;
275
283
  interface AgentToolConfig {
276
284
  enabled?: boolean;
@@ -279,6 +287,15 @@ interface AgentToolConfig {
279
287
  config?: Record<string, unknown>;
280
288
  [key: string]: unknown;
281
289
  }
290
+ type AgentMcpServersConfig = Record<string, AgentMcpServerConfig>;
291
+ interface AgentMcpServerConfig {
292
+ enabled?: boolean;
293
+ /** Applies to every tool the server exposes. */
294
+ needsApproval?: boolean;
295
+ /** Extra request headers; values resolved from account env vars at sync. */
296
+ headers?: Record<string, string>;
297
+ [key: string]: unknown;
298
+ }
282
299
  interface AgentChannelsConfig {
283
300
  telegram?: AgentTelegramChannelConfig;
284
301
  github?: AgentGitHubChannelConfig;
@@ -385,7 +402,9 @@ interface AgentZaloChannelConfig {
385
402
  }
386
403
 
387
404
  /**
388
- * Cron-job records, input normalization, and patch-merge helpers.
405
+ * Cron-job records and patch-merge helpers for the runtime. Input
406
+ * normalization lives in the config plane (packages/convex/model/cronRules.ts,
407
+ * the single home of those rules).
389
408
  */
390
409
 
391
410
  type CronStatus = "active" | "paused";
@@ -427,48 +446,45 @@ type UpdateCronInput = {
427
446
  });
428
447
 
429
448
  /**
430
- * Predefined sandbox sizes — the canonical (vcpu, memoryMb, storageGb) catalog
431
- * shared by sandbox config validation, the workdir resource mapping, and the
432
- * Convex `sandboxInstances` mirror. Sizes are the user-facing knob (`config.size`)
433
- * that reconciles issue #78's tiers with each backend's real limits.
434
- *
435
- * The specs are canonical/advisory: workdir applies them as create-time resources
436
- * (clamping vcpu to its allowed set); MicroVM bakes size into the image so the
437
- * specs are display-only there; daytona/e2b/vercel size natively. The control-plane
438
- * mirror type lives here too so the Convex writer and the executors share one shape
439
- * without importing across the _shared/harness boundary.
449
+ * Sandbox-config validation for the Convex config plane. Ports core's former
450
+ * storage/sandbox-config.ts normalizer so the public /v1/sandboxes contract
451
+ * is unchanged. The public projection lives in ./responses.ts.
440
452
  */
441
-
442
- type SandboxSize = "tiny" | "xsmall" | "small" | "medium" | "large";
443
-
453
+ declare const SANDBOX_PROVIDERS: readonly ["sandbox", "lambda", "e2b", "daytona", "vercel"];
454
+ declare const SANDBOX_RUNTIMES: readonly ["bash", "python", "node"];
455
+ declare const SANDBOX_PERMISSION_MODES: readonly ["edit", "ask", "bypass"];
456
+ declare const SANDBOX_NETWORK_MODES: readonly ["allow-all", "deny-all", "restricted"];
457
+ declare const SANDBOX_SIZE_NAMES: readonly ["tiny", "xsmall", "small", "medium", "large"];
458
+ type SandboxProvider = (typeof SANDBOX_PROVIDERS)[number];
459
+ type RuntimeName = (typeof SANDBOX_RUNTIMES)[number];
460
+ type PermissionMode = (typeof SANDBOX_PERMISSION_MODES)[number];
461
+ type NetworkMode = (typeof SANDBOX_NETWORK_MODES)[number];
462
+ type SandboxSize = (typeof SANDBOX_SIZE_NAMES)[number];
444
463
  /**
445
- * Sandbox config: account-scoped, reusable sandbox definitions referenced by
446
- * agents via `config.sandbox`. A sandbox is a collection of Claude-Code-style
447
- * tools (bash/read/write/edit/glob/grep) backed by a provider. Validation and
448
- * the public projection live here.
449
- * Stored encrypted at rest because `envVars`/`options` may hold secrets.
464
+ * Idle and maximum lifetime controls for a persistent sandbox.
450
465
  */
451
-
452
- type SandboxProvider = "sandbox" | "lambda" | "e2b" | "daytona" | "vercel";
453
- type SandboxRuntimeName = "bash" | "python" | "node";
454
- type SandboxPermissionMode = "edit" | "ask" | "bypass";
455
- type SandboxNetworkMode = "allow-all" | "deny-all" | "restricted";
456
466
  interface SandboxLifecycleConfig {
457
467
  idleTimeoutSeconds?: number;
458
468
  maxLifetimeSeconds?: number;
459
469
  }
470
+ /**
471
+ * Provider-normalized sandbox network policy.
472
+ */
460
473
  interface SandboxNetworkConfig {
461
- mode: SandboxNetworkMode;
474
+ mode: NetworkMode;
462
475
  allowDomains?: string[];
463
476
  allowCidrs?: string[];
464
477
  }
478
+ /**
479
+ * Account-scoped reusable sandbox configuration referenced by agents.
480
+ */
465
481
  interface SandboxConfig {
466
482
  provider: SandboxProvider;
467
483
  size?: SandboxSize;
468
484
  snapshot?: string;
469
- runtimes?: SandboxRuntimeName[];
485
+ runtimes?: RuntimeName[];
470
486
  network?: SandboxNetworkConfig;
471
- permissionMode?: SandboxPermissionMode;
487
+ permissionMode?: PermissionMode;
472
488
  persistent?: boolean;
473
489
  lifecycle?: SandboxLifecycleConfig;
474
490
  onCreate?: string[];
@@ -481,12 +497,12 @@ interface SandboxConfig {
481
497
  }
482
498
 
483
499
  /**
484
- * Workspace config: account-scoped, reusable workspace definitions referenced by
485
- * agents via `config.workspaces[].workspaceId`. A workspace is the persistent
486
- * S3-backed filesystem mounted into a sandbox; agents referencing the same
487
- * workspaceId share the same files. Holds no secrets, so it is stored in
488
- * plaintext (unlike sandbox config). Validation and the public projection live
489
- * here.
500
+ * Workspace-config validation for the Convex config plane. Ports core's
501
+ * former storage/workspace-config.ts normalizer so the public /v1/workspaces
502
+ * contract is unchanged. Workspace config holds no secrets (a roleArn is not
503
+ * a secret), so it is stored and returned in plaintext. Pure module — safe
504
+ * for the default Convex runtime. The public projection lives in
505
+ * ./responses.ts.
490
506
  */
491
507
  declare const WORKSPACE_STORAGE_PROVIDERS: readonly ["s3"];
492
508
  type WorkspaceStorageProvider = (typeof WORKSPACE_STORAGE_PROVIDERS)[number];
@@ -505,43 +521,72 @@ interface WorkspaceStorageConfig {
505
521
  prefix?: string;
506
522
  auth?: WorkspaceStorageAuth;
507
523
  }
508
- interface WorkspaceHarnessConfig {
509
- workspace?: {
510
- enabled?: boolean;
511
- };
512
- memory?: {
513
- enabled?: boolean;
514
- };
515
- }
516
524
  interface WorkspaceConfig {
517
525
  storage: WorkspaceStorageConfig;
518
526
  isolation?: boolean;
519
- harness?: WorkspaceHarnessConfig;
527
+ harness?: {
528
+ workspace?: {
529
+ enabled?: boolean;
530
+ };
531
+ memory?: {
532
+ enabled?: boolean;
533
+ };
534
+ };
520
535
  }
521
536
 
522
537
  /**
523
- * Agent policy contracts and validation.
524
- * Runtime decisions are made by OPA using the same document/input shape.
538
+ * Agent-policy validation for the Convex config plane. Ports core's public
539
+ * CRUD normalizer so policy documents keep the account-management API
540
+ * contract. The public projection lives in ./responses.ts.
541
+ */
542
+ declare const AGENT_POLICY_ACTIONS: readonly ["agent.invoke", "tool.call", "workspace.read", "workspace.write", "workspace.exec", "subagent.run", "skill.load"];
543
+ /**
544
+ * API action namespace for account roles: one read/write pair per config-plane
545
+ * resource route. Roles carry the same PolicyDocument shape as agent policies;
546
+ * each caller passes its action set to `normalizePolicyDocument`.
547
+ */
548
+ declare const API_POLICY_ACTIONS: readonly ["account:read", "account:write", "agents:read", "agents:write", "channels:read", "channels:write", "crons:read", "crons:write", "env:read", "env:write", "hooks:read", "hooks:write", "mcp:read", "mcp:write", "policies:read", "policies:write", "sandboxes:read", "sandboxes:write", "skills:read", "skills:write", "tools:read", "tools:write", "workspaces:read", "workspaces:write"];
549
+ type AgentPolicyAction = (typeof AGENT_POLICY_ACTIONS)[number];
550
+ type ApiPolicyAction = (typeof API_POLICY_ACTIONS)[number];
551
+ type PolicyAction = AgentPolicyAction | ApiPolicyAction;
552
+ /**
553
+ * One optional predicate on a policy rule.
525
554
  */
526
- declare const POLICY_ACTIONS: readonly ["agent.invoke", "tool.call", "workspace.read", "workspace.write", "workspace.exec", "subagent.run", "skill.load"];
527
- type PolicyAction = (typeof POLICY_ACTIONS)[number];
528
- type PolicyEffect = "allow" | "deny";
529
- type PolicyMode = "enforce" | "audit";
530
- type PolicyConditionOperator = "equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains";
531
555
  interface PolicyCondition {
532
556
  attribute: string;
533
557
  operator: PolicyConditionOperator;
534
558
  value: string | number | boolean | string[] | number[] | boolean[];
535
559
  }
560
+ type PolicyConditionOperator = "equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains";
561
+ /**
562
+ * Versioned policy document accepted by account-management CRUD.
563
+ */
564
+ interface PolicyDocument {
565
+ version: 1;
566
+ /** How hard this policy bites where it is attached. Omitted reads as `audit`. */
567
+ mode?: "enforce" | "audit";
568
+ rules: PolicyRule[];
569
+ }
570
+ type PolicyEffect = "allow" | "deny";
571
+ /**
572
+ * Resource selector fields supported by policy rules.
573
+ */
536
574
  interface PolicyResourceSelector {
537
575
  toolNames?: string[];
538
576
  toolIds?: string[];
577
+ /** MCP registration ids, for scoping tool.call rules per server (#331). */
578
+ mcpIds?: string[];
539
579
  workspaceIds?: string[];
540
580
  workspaceNames?: string[];
541
581
  filePaths?: string[];
542
582
  subagentIds?: string[];
543
583
  skillPaths?: string[];
584
+ /** Config-plane resource ids for API-action rules; "*" matches every id. */
585
+ resourceIds?: string[];
544
586
  }
587
+ /**
588
+ * One allow/deny rule inside a policy document.
589
+ */
545
590
  interface PolicyRule {
546
591
  id: string;
547
592
  effect: PolicyEffect;
@@ -549,26 +594,13 @@ interface PolicyRule {
549
594
  resources?: PolicyResourceSelector;
550
595
  conditions?: PolicyCondition[];
551
596
  }
552
- interface PolicyDocument {
553
- version: 1;
554
- /**
555
- * How hard this policy bites. `audit` records what it would have done and
556
- * blocks nothing; `enforce` lets its deny rules refuse, and switches the
557
- * places it is attached to over to default-deny. Omitted reads as `audit`,
558
- * so a freshly written policy can never break a running agent.
559
- */
560
- mode?: PolicyMode;
561
- rules: PolicyRule[];
562
- }
563
597
 
564
598
  /**
565
- * Channel records: an account-scoped row per real place a team talks — one Slack
566
- * channel, one Discord channel, one repository. It binds that place to an agent
567
- * and adds instructions, workspaces, policies and roles scoped to it.
568
- * Distinct from `config.channels`, which holds one adapter's credentials.
569
- * A record narrows and adds; it never grants capability the agent lacks.
599
+ * Channel record validation for the config plane — the single home of the
600
+ * normalizers core's `shared/domain/channel-record.ts` re-exports. Kept free
601
+ * of Convex imports so the rules stay unit-testable; the public projection
602
+ * lives in ./responses.ts.
570
603
  */
571
-
572
604
  /** Where a reply lands: its own thread, or wherever the message came from. */
573
605
  declare const CHANNEL_REPLY_TARGETS: readonly ["thread", "source"];
574
606
  type ChannelReplyIn = (typeof CHANNEL_REPLY_TARGETS)[number];
@@ -636,8 +668,10 @@ interface Skill {
636
668
  * server runtimes, as well as Node and Bun.
637
669
  *
638
670
  * Auth: every call sends `Authorization: Bearer {accountSecret}` to
639
- * `{baseUrl}/v1/...`. Secrets inside agent configs are encrypted at rest by
640
- * the platform and come back redacted (`********`) on reads.
671
+ * `{baseUrl}/v1/...` — or a short-lived `fp_sts_` role session token from
672
+ * `assumeRole()`, limited to what the role's policy allows. Secrets inside
673
+ * agent configs are encrypted at rest by the platform and come back redacted
674
+ * (`********`) on reads.
641
675
  */
642
676
 
643
677
  interface BroodsAccountClientOptions {
@@ -645,6 +679,12 @@ interface BroodsAccountClientOptions {
645
679
  baseUrl?: string;
646
680
  /** Account secret used as the Bearer token. Falls back to `BROODS_ACCOUNT_SECRET`. */
647
681
  accountSecret?: string;
682
+ /**
683
+ * Short-lived `fp_sts_` role session token (from {@link BroodsAccountClient.assumeRole})
684
+ * used as the Bearer instead of the account secret. The session can only do
685
+ * what its role's policy allows. Falls back to `BROODS_SESSION_TOKEN`.
686
+ */
687
+ sessionToken?: string;
648
688
  fetch?: typeof fetch;
649
689
  }
650
690
  /** Public account record returned by `GET /v1/account`. */
@@ -724,6 +764,29 @@ interface AccountPolicy {
724
764
  createdAt: string;
725
765
  updatedAt: string;
726
766
  }
767
+ /**
768
+ * Public account-role record returned by the roles routes. The policy uses the
769
+ * API action namespace (`"agents:read"`, `"crons:write"`, ...); `projectId` and
770
+ * `stageId` bound which stage runtime keys may assume the role.
771
+ */
772
+ interface AccountRole {
773
+ accountId: string;
774
+ roleId: string;
775
+ name: string;
776
+ projectId?: string;
777
+ stageId?: string;
778
+ status: "active" | "disabled";
779
+ policy: PolicyDocument;
780
+ createdAt: string;
781
+ updatedAt: string;
782
+ }
783
+ /** Short-lived role session minted by `POST /v1/account/assume-role`. */
784
+ interface AssumeRoleResult {
785
+ /** `fp_sts_` bearer token; pass it as `sessionToken` to a new client. */
786
+ token: string;
787
+ /** ISO timestamp when the session stops working. */
788
+ expiresAt: string;
789
+ }
727
790
  /**
728
791
  * One real place a team talks — a Slack channel, a Discord channel, a repo —
729
792
  * bound to an agent. The runtime reads it on the inbound webhook to decide who
@@ -817,6 +880,46 @@ interface UpdateToolInput {
817
880
  runtime?: "isolate" | "sandbox";
818
881
  defaultConfig?: unknown;
819
882
  }
883
+ /** Public MCP server registration returned by the `/v1/mcp` routes (#331). */
884
+ interface AccountMcpServer {
885
+ accountId: string;
886
+ serverId: string;
887
+ projectId: string;
888
+ stageId: string;
889
+ name: string;
890
+ description?: string;
891
+ transport: "http" | "hosted";
892
+ /** External servers only; a hosted row has no endpoint of its own. */
893
+ url?: string;
894
+ /** Hosted servers only: content hash of the uploaded bundle. */
895
+ sha256?: string;
896
+ headers?: Record<string, string>;
897
+ allowedTools?: string[];
898
+ disabled: boolean;
899
+ status: string;
900
+ createdAt: string;
901
+ updatedAt: string;
902
+ deletedAt?: string;
903
+ }
904
+ /** Fields accepted by `POST /v1/mcp`: `url` connects, `bundle` uploads. */
905
+ interface CreateMcpServerInput {
906
+ name: string;
907
+ description?: string;
908
+ url?: string;
909
+ bundle?: string;
910
+ headers?: Record<string, string>;
911
+ allowedTools?: string[];
912
+ }
913
+ /** Fields accepted by `PATCH /v1/mcp/{serverId}`; every field is optional. */
914
+ interface UpdateMcpServerInput {
915
+ name?: string;
916
+ description?: string;
917
+ url?: string;
918
+ bundle?: string;
919
+ headers?: Record<string, string>;
920
+ allowedTools?: string[];
921
+ disabled?: boolean;
922
+ }
820
923
  /**
821
924
  * Body of a skill upload (`POST /v1/skills`, `PUT /v1/skills/{skillName}`).
822
925
  * `json` needs `name`/`description`/`content`; `files` needs base64 `files`
@@ -874,7 +977,7 @@ declare class BroodsAccountApiError extends Error {
874
977
  */
875
978
  declare class BroodsAccountClient {
876
979
  private readonly baseUrl;
877
- private readonly accountSecret;
980
+ private readonly bearerToken;
878
981
  private readonly fetchImpl;
879
982
  constructor(options?: BroodsAccountClientOptions);
880
983
  /** The account this secret belongs to. Its `accountId` is the first segment of channel webhook URLs. */
@@ -884,6 +987,15 @@ declare class BroodsAccountClient {
884
987
  username?: string;
885
988
  description?: string | null;
886
989
  }): Promise<BroodsAccount | null>;
990
+ /**
991
+ * Exchange a role for a short-lived `fp_sts_` session token. Callable with
992
+ * the account secret, a CLI login token, or a stage runtime key (the latter
993
+ * only into roles scoped to the key's own project/stage). Construct a new
994
+ * client with `{ sessionToken: result.token }` to act as the role.
995
+ */
996
+ assumeRole(roleId: string, options?: {
997
+ ttlSeconds?: number;
998
+ }): Promise<AssumeRoleResult>;
887
999
  /** Rotate the account secret. The returned `secret` is shown once and the current secret stops working immediately, so persist it before the process exits. */
888
1000
  rotateSecret(): Promise<RotateSecretResult>;
889
1001
  /** Delete this account and cascade-clean every account-scoped resource. `cleanup` reports per-resource deletion counts. */
@@ -979,6 +1091,12 @@ declare class BroodsAccountClient {
979
1091
  /** PATCH an uploaded tool. Omitting `bundle` keeps the stored bundle and runtime. Returns null when the tool is gone. */
980
1092
  updateTool(toolId: string, patch: UpdateToolInput): Promise<AccountTool | null>;
981
1093
  deleteTool(toolId: string): Promise<boolean>;
1094
+ /** MCP servers are stage-scoped like tools; both scope fields are required. */
1095
+ listMcpServers(scope: ToolScope): Promise<AccountMcpServer[]>;
1096
+ createMcpServer(scope: ToolScope, input: CreateMcpServerInput): Promise<AccountMcpServer>;
1097
+ getMcpServer(serverId: string): Promise<AccountMcpServer | null>;
1098
+ updateMcpServer(serverId: string, patch: UpdateMcpServerInput): Promise<AccountMcpServer | null>;
1099
+ deleteMcpServer(serverId: string): Promise<boolean>;
982
1100
  listPolicies(): Promise<AccountPolicy[]>;
983
1101
  createPolicy(input: {
984
1102
  name: string;
@@ -994,6 +1112,23 @@ declare class BroodsAccountClient {
994
1112
  status?: string;
995
1113
  }): Promise<AccountPolicy | null>;
996
1114
  deletePolicy(policyId: string): Promise<boolean>;
1115
+ /** Roles are account-secret only: a session cannot list, mint, or edit roles. */
1116
+ listRoles(): Promise<AccountRole[]>;
1117
+ /** Create a role whose policy uses the API action namespace. `projectId`/`stageId` must be provided together. */
1118
+ createRole(input: {
1119
+ name: string;
1120
+ policy: PolicyDocument;
1121
+ projectId?: string;
1122
+ stageId?: string;
1123
+ }): Promise<AccountRole>;
1124
+ getRole(roleId: string): Promise<AccountRole | null>;
1125
+ /** PATCH a role. `status: "disabled"` kills every live session of the role. Returns null when the role is gone. */
1126
+ updateRole(roleId: string, patch: {
1127
+ name?: string;
1128
+ policy?: PolicyDocument;
1129
+ status?: "active" | "disabled";
1130
+ }): Promise<AccountRole | null>;
1131
+ deleteRole(roleId: string): Promise<boolean>;
997
1132
  listChannels(): Promise<AccountChannel[]>;
998
1133
  /** Bind one real chat channel to an agent. One active record per place. */
999
1134
  createChannel(input: {
@@ -1028,4 +1163,4 @@ declare class BroodsAccountClient {
1028
1163
  }
1029
1164
 
1030
1165
  export { BroodsAccountApiError, BroodsAccountClient, envPlaceholder };
1031
- export type { AccountAgent, AccountChannel, AccountEnvVar, AccountPolicy, AccountSandbox, AccountTool, AccountWorkspace, BroodsAccount, BroodsAccountClientOptions, ChannelRecordConfig, CreateAgentResult, CreateToolInput, DeleteAccountResult, RotateSecretResult, SandboxLifecycleResult, SandboxSnapshotResult, SandboxTerminalTicket, SkillUploadInput, ToolScope, UpdateAgentInput, UpdateToolInput, WorkspaceFileEntry };
1166
+ export type { AccountAgent, AccountChannel, AccountEnvVar, AccountMcpServer, AccountPolicy, AccountRole, AccountSandbox, AccountTool, AccountWorkspace, AssumeRoleResult, BroodsAccount, BroodsAccountClientOptions, ChannelRecordConfig, CreateAgentResult, CreateMcpServerInput, CreateToolInput, DeleteAccountResult, RotateSecretResult, SandboxLifecycleResult, SandboxSnapshotResult, SandboxTerminalTicket, SkillUploadInput, ToolScope, UpdateAgentInput, UpdateMcpServerInput, UpdateToolInput, WorkspaceFileEntry };
package/dist/account.js CHANGED
@@ -1 +1 @@
1
- import{S,r,y}from"./chunk-vyvy543y.js";export{r as BroodsAccountApiError,y as BroodsAccountClient,S as envPlaceholder};
1
+ import{a,b,c}from"./chunk-36yhe6qb.js";import"./chunk-4sdsq7gb.js";export{b as BroodsAccountApiError,c as BroodsAccountClient,a as envPlaceholder};
@@ -0,0 +1,2 @@
1
+ var c=/^[A-Z][A-Z0-9_]*$/;function u(e){if(!c.test(e)||e.length>64)throw Error("envPlaceholder name must match /^[A-Z][A-Z0-9_]*$/ and be at most 64 characters.");return`\${${e}}`}class r extends Error{status;body;constructor(e,t,n,s){super(`${e} ${t} failed with ${n}: ${s}`);this.name="BroodsAccountApiError",this.status=n,this.body=s}}function i(e){return typeof process<"u"?process?.env?.[e]:void 0}function o(e){return`?${new URLSearchParams({project:e.project,stage:e.stage}).toString()}`}class l{baseUrl;bearerToken;fetchImpl;constructor(e={}){let t=e.baseUrl??i("BROODS_BASE_URL")??"https://gateway.broods.app",n=e.sessionToken??e.accountSecret??i("BROODS_SESSION_TOKEN")??i("BROODS_ACCOUNT_SECRET");if(!n)throw Error("BroodsAccountClient requires an accountSecret or sessionToken (or BROODS_ACCOUNT_SECRET / BROODS_SESSION_TOKEN).");let s=t.length;while(s>0&&t.charCodeAt(s-1)===47)s-=1;this.baseUrl=t.slice(0,s),this.bearerToken=n,this.fetchImpl=e.fetch??fetch}async getAccount(){let e=await this.request("GET","/v1/account");if(!e)throw new r("GET","/v1/account",404,"Account not found");return e.account}async updateAccount(e){return(await this.request("PATCH","/v1/account",e))?.account??null}async assumeRole(e,t={}){let n=await this.request("POST","/v1/account/assume-role",{roleId:e,...t.ttlSeconds!==void 0?{ttlSeconds:t.ttlSeconds}:{}});if(!n)throw new r("POST","/v1/account/assume-role",404,"Role not found");return n}async rotateSecret(){let e=await this.request("POST","/v1/account/rotate-secret");if(!e)throw new r("POST","/v1/account/rotate-secret",404,"Account not found");return e}async deleteAccount(){return await this.request("DELETE","/v1/account")??{deleted:!1}}webhookUrl(e,t){let n=[e,t].map(encodeURIComponent);return`${this.baseUrl}/webhooks/${n.join("/")}`}async listAgents(){return(await this.request("GET","/v1/agents"))?.agents??[]}async createAgent(e){let t=await this.request("POST","/v1/agents",e);if(!t)throw new r("POST","/v1/agents",404,"Not found");return t}async getAgent(e){return await this.request("GET",`/v1/agents/${encodeURIComponent(e)}`)}async updateAgent(e,t){return await this.request("PATCH",`/v1/agents/${encodeURIComponent(e)}`,t)}async deleteAgent(e){return(await this.request("DELETE",`/v1/agents/${encodeURIComponent(e)}`))?.deleted??!1}async listEnvVars(){return(await this.request("GET","/v1/env"))?.env??[]}async setEnvVar(e,t){await this.request("PUT",`/v1/env/${encodeURIComponent(e)}`,{value:t})}async deleteEnvVar(e){return(await this.request("DELETE",`/v1/env/${encodeURIComponent(e)}`))?.deleted??!1}async listCrons(){return(await this.request("GET","/v1/crons"))?.crons??[]}async createCron(e){let t=await this.request("POST","/v1/crons",e);if(!t)throw new r("POST","/v1/crons",404,"Not found");return t}async getCron(e){return await this.request("GET",`/v1/crons/${encodeURIComponent(e)}`)}async updateCron(e,t){return await this.request("PATCH",`/v1/crons/${encodeURIComponent(e)}`,t)}async deleteCron(e){return(await this.request("DELETE",`/v1/crons/${encodeURIComponent(e)}`))?.deleted??!1}async listCronRuns(e,t={}){let n=t.limit!==void 0?`?limit=${t.limit}`:"";return(await this.request("GET",`/v1/crons/${encodeURIComponent(e)}/runs${n}`))?.runs??[]}async listWorkspaces(){return(await this.request("GET","/v1/workspaces"))?.workspaces??[]}async createWorkspace(e){let t=await this.request("POST","/v1/workspaces",e);if(!t)throw new r("POST","/v1/workspaces",404,"Not found");return t}async getWorkspace(e){return await this.request("GET",`/v1/workspaces/${encodeURIComponent(e)}`)}async updateWorkspace(e,t){return await this.request("PATCH",`/v1/workspaces/${encodeURIComponent(e)}`,t)}async deleteWorkspace(e){return(await this.request("DELETE",`/v1/workspaces/${encodeURIComponent(e)}`))?.deleted??!1}async listWorkspaceFiles(e){return(await this.request("GET",`/v1/workspaces/${encodeURIComponent(e)}/files`))?.files??[]}async getWorkspaceFileUrl(e,t){let n=`?path=${encodeURIComponent(t)}`;return(await this.request("GET",`/v1/workspaces/${encodeURIComponent(e)}/files${n}`))?.url??null}async uploadWorkspaceFile(e,t){let n=`/v1/workspaces/${encodeURIComponent(e)}/files`,s=await this.request("POST",n,t);if(!s)throw new r("POST",n,404,"Workspace not found");return s.file}async renameWorkspaceFile(e,t,n){return(await this.request("PATCH",`/v1/workspaces/${encodeURIComponent(e)}/files`,{path:t,newPath:n}))?.renamed??!1}async deleteWorkspaceFile(e,t){return(await this.request("DELETE",`/v1/workspaces/${encodeURIComponent(e)}/files`,{path:t}))?.deleted??!1}async listSandboxes(){return(await this.request("GET","/v1/sandboxes"))?.sandboxes??[]}async createSandbox(e){let t=await this.request("POST","/v1/sandboxes",e);if(!t)throw new r("POST","/v1/sandboxes",404,"Not found");return t}async getSandbox(e){return await this.request("GET",`/v1/sandboxes/${encodeURIComponent(e)}`)}async updateSandbox(e,t){return await this.request("PATCH",`/v1/sandboxes/${encodeURIComponent(e)}`,t)}async deleteSandbox(e){return(await this.request("DELETE",`/v1/sandboxes/${encodeURIComponent(e)}`))?.deleted??!1}async suspendSandbox(e,t){return await this.sandboxAction(e,"suspend",{reservationKey:t})}async resumeSandbox(e,t){return await this.sandboxAction(e,"resume",{reservationKey:t})}async terminateSandbox(e,t){return await this.sandboxAction(e,"terminate",{reservationKey:t})}async snapshotSandbox(e,t,n){return await this.sandboxAction(e,"snapshot",{reservationKey:t,name:n})}async openSandboxTerminal(e,t){return await this.sandboxAction(e,"terminal",{reservationKey:t})}async listTools(e){let t=`/v1/tools${o(e)}`,n=await this.request("GET",t);if(!n)throw new r("GET",t,404,"Not found");return n.tools??[]}async createTool(e,t){let n=`/v1/tools${o(e)}`,s=await this.request("POST",n,t);if(!s)throw new r("POST",n,404,"Not found");return s}async getTool(e){return await this.request("GET",`/v1/tools/${encodeURIComponent(e)}`)}async updateTool(e,t){return await this.request("PATCH",`/v1/tools/${encodeURIComponent(e)}`,t)}async deleteTool(e){return(await this.request("DELETE",`/v1/tools/${encodeURIComponent(e)}`))?.deleted??!1}async listMcpServers(e){let t=`/v1/mcp${o(e)}`,n=await this.request("GET",t);if(!n)throw new r("GET",t,404,"Not found");return n.servers??[]}async createMcpServer(e,t){let n=`/v1/mcp${o(e)}`,s=await this.request("POST",n,t);if(!s)throw new r("POST",n,404,"Not found");return s}async getMcpServer(e){return await this.request("GET",`/v1/mcp/${encodeURIComponent(e)}`)}async updateMcpServer(e,t){return await this.request("PATCH",`/v1/mcp/${encodeURIComponent(e)}`,t)}async deleteMcpServer(e){return(await this.request("DELETE",`/v1/mcp/${encodeURIComponent(e)}`))?.deleted??!1}async listPolicies(){return(await this.request("GET","/v1/policies"))?.policies??[]}async createPolicy(e){let t=await this.request("POST","/v1/policies",e);if(!t)throw new r("POST","/v1/policies",404,"Not found");return t}async getPolicy(e){return await this.request("GET",`/v1/policies/${encodeURIComponent(e)}`)}async updatePolicy(e,t){return await this.request("PATCH",`/v1/policies/${encodeURIComponent(e)}`,t)}async deletePolicy(e){return(await this.request("DELETE",`/v1/policies/${encodeURIComponent(e)}`))?.deleted??!1}async listRoles(){return(await this.request("GET","/v1/roles"))?.roles??[]}async createRole(e){let t=await this.request("POST","/v1/roles",e);if(!t)throw new r("POST","/v1/roles",404,"Not found");return t}async getRole(e){return await this.request("GET",`/v1/roles/${encodeURIComponent(e)}`)}async updateRole(e,t){return await this.request("PATCH",`/v1/roles/${encodeURIComponent(e)}`,t)}async deleteRole(e){return(await this.request("DELETE",`/v1/roles/${encodeURIComponent(e)}`))?.deleted??!1}async listChannels(){return(await this.request("GET","/v1/channels"))?.channels??[]}async createChannel(e){let t=await this.request("POST","/v1/channels",e);if(!t)throw new r("POST","/v1/channels",404,"Not found");return t}async getChannel(e){return await this.request("GET",`/v1/channels/${encodeURIComponent(e)}`)}async updateChannel(e,t){return await this.request("PATCH",`/v1/channels/${encodeURIComponent(e)}`,t)}async deleteChannel(e){return(await this.request("DELETE",`/v1/channels/${encodeURIComponent(e)}`))?.deleted??!1}async listSkills(){return(await this.request("GET","/v1/skills"))?.skills??[]}async createSkill(e){let t=await this.request("POST","/v1/skills",e);if(!t)throw new r("POST","/v1/skills",404,"Not found");return t}async getSkill(e){return await this.request("GET",`/v1/skills/${encodeURIComponent(e)}`)}async uploadSkill(e,t){let n=`/v1/skills/${encodeURIComponent(e)}`,s=await this.request("PUT",n,t);if(!s)throw new r("PUT",n,404,"Skill not found");return s}async deleteSkill(e){return(await this.request("DELETE",`/v1/skills/${encodeURIComponent(e)}`))?.deleted??!1}async sandboxAction(e,t,n){let s=`/v1/sandboxes/${encodeURIComponent(e)}/${t}`,a=await this.request("POST",s,n);if(!a)throw new r("POST",s,404,"Sandbox not found");return a}async request(e,t,n){let s=await this.fetchImpl(`${this.baseUrl}${t}`,{method:e,headers:{Authorization:`Bearer ${this.bearerToken}`,...n!==void 0?{"Content-Type":"application/json"}:{}},...n!==void 0?{body:JSON.stringify(n)}:{}});if(s.status===404)return null;if(!s.ok)throw new r(e,t,s.status,await s.text());return await s.json()}}
2
+ export{u as a,r as b,l as c};
@@ -1,2 +1,2 @@
1
- var d=Object.defineProperty;var e=(a)=>a;function f(a,b){this[a]=e.bind(null,b)}var I=(a,b)=>{for(var c in b)d(a,c,{get:b[c],enumerable:!0,configurable:!0,set:f.bind(b,c)})};
2
- export{I};
1
+ var d=Object.defineProperty;var e=(a)=>a;function f(a,b){this[a]=e.bind(null,b)}var g=(a,b)=>{for(var c in b)d(a,c,{get:b[c],enumerable:!0,configurable:!0,set:f.bind(b,c)})};
2
+ export{g as V};
@@ -0,0 +1,5 @@
1
+ function k(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*D(e){let n=e.getReader(),t=new TextDecoder,o="";try{while(!0){let{done:r,value:s}=await n.read();if(r)break;o+=t.decode(s,{stream:!0});let i=o.split(`
2
+ `);o=i.pop()??"";for(let a of i)if(a.startsWith("data: "))yield a.slice(6)}}finally{n.releaseLock()}}import{mkdir as Q,readFile as ee,writeFile as ne}from"node:fs/promises";import{homedir as te}from"node:os";import{dirname as oe,join as re}from"node:path";var Ee="broods",De="_generated",h=re(te(),".broods","config.json"),N="broods.app",T="dashboard",se="gateway";async function Ne(){let e=process.env.BROODS_TOKEN,n=process.env.BROODS_BASE_URL;if(e&&n)return{baseUrl:d(n),token:e,createdAt:new Date().toISOString()};try{let t=JSON.parse(await ee(h,"utf8"));if(typeof t.baseUrl!=="string"||!t.baseUrl)return null;return t}catch{return null}}async function Te(e){await Q(oe(h),{recursive:!0}),await ne(h,`${JSON.stringify(e,null,2)}
3
+ `,"utf8")}function O(){return process.env.BROODS_STAGE}function d(e){let n=e.length;while(n>0&&e.charCodeAt(n-1)===47)n-=1;return e.slice(0,n)}function Oe(e){try{let n=new URL(e);if(!n.hostname.startsWith(`${T}.`))return;let t=n.hostname.slice(T.length+1);if(t!==N&&!t.endsWith(`.${N}`))return;return n.hostname=`${se}.${t}`,d(n.origin)}catch{return}}import{existsSync as U,readFileSync as E,statSync as ie}from"node:fs";import{join as W,resolve as M}from"node:path";var v=new Map,ae=new Set(Object.keys(process.env)),P=new Set;function I(e=process.cwd()){ce(e);let n=de();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:O()}}function $e(e,n=process.cwd()){let t=process.env[e];if(t===void 0)return!1;let o=M(n);for(let r of[".env.local",".env"]){let s=W(o,r);if(!U(s))continue;let i=_(E(s,"utf8"));if(i[e]!==void 0)return i[e]!==t}return!0}function ce(e){let n=M(e),t=process.env.BROODS_RELOAD_ENV==="1";for(let o of[".env",".env.local"]){let r=W(n,o);if(!U(r)){v.delete(r);continue}let s=ie(r).mtimeMs,i=v.get(r);if(i&&i.mtimeMs===s)continue;v.set(r,{mtimeMs:s});for(let[a,u]of Object.entries(_(E(r,"utf8"))))if(t||!ae.has(a)||P.has(a))process.env[a]=u,P.add(a)}}function _(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]=ue(r.slice(s+1).trim())}return n}function ue(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 de(){try{let e=JSON.parse(E(h,"utf8"));if(typeof e.baseUrl!=="string"||typeof e.token!=="string")return null;return{baseUrl:d(e.baseUrl),...typeof e.dashboardUrl==="string"?{dashboardUrl:d(e.dashboardUrl)}:{},token:e.token}}catch{return null}}var x="https://gateway.broods.app";class B extends Error{accepted;constructor(e){super(`Ingress ${e.eventId} was accepted asynchronously with status ${e.status??"queued"}`);this.accepted=e;this.name="IngressAcceptedError"}}class le{baseUrl;apiKey;fetchImpl;constructor(e={}){I(),this.baseUrl=A(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||x),this.apiKey=e.apiKey||process.env.BROODS_API_KEY||void 0,this.fetchImpl=e.fetch??fetch}channelWebhookUrl(e){return`${this.baseUrl}${e.webhookPath.startsWith("/")?"":"/"}${e.webhookPath}`}accountWebhookUrl(e,n){let t=[e,n].map(encodeURIComponent);return`${this.baseUrl}/webhooks/${t.join("/")}`}stageWebhookUrl(e,n,t){let o=[e,"dev",n,t].map(encodeURIComponent);return`${this.baseUrl}/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}),stream:(s)=>this.stream({...s,agentName:o,agentId:r})}}let t=e;if(!t.id)throw Error(`Agent ${t.name} is missing a generated id. Run broods dev --once or broods deploy to sync it first.`);return{id:t.id,run:(o)=>this.run(t,o),runAsync:(o)=>this.runAsync(t,o),stream:(o)=>this.stream(t,o)}}async run(e,n){let t=[],o="",r=n?this.stream(e,n):this.stream(e);for await(let s of r)if(t.push(s),s.type==="text-delta")o+=s.text;return{text:o,events:t}}async*stream(e,n){let t=n?{...n,agentId:e.id,agentName:e.name}:e,o=K(t,"cli"),r=n?this.scopedUrl(e):this.baseUrl,s=await this.openStream(o,r);if(!s.ok)throw Error(`Run failed: ${s.status} ${await s.text()}`);if(s.status===202)throw new B(H(await s.json(),o));if(!s.body)throw Error("Run response has no body");for await(let i of D(s.body)){let a;try{a=JSON.parse(i)}catch{continue}if(a.type==="error")throw Error(`Agent run failed: ${he(a.error)}`);yield a}}async runAsync(e,n){let t=n?{...n,agentId:e.id,agentName:e.name}:e,o=K(t,"async"),r=n?this.scopedUrl(e,"/async"):`${this.baseUrl}/async`,s=await this.fetchJson(r,{method:"POST",headers:this.apiKeyHeaders(),body:JSON.stringify(o)});if(s.status!==202)throw Error(`Async run failed: ${s.status} ${await fe(s,"202 JSON")}`);let i=H(await s.json(),o);return{...i,conversationKey:o.conversationKey,poll:()=>this.getAsyncStatus(i),wait:(a)=>this.waitForAsyncStatus(i,a)}}async getAsyncStatus(e,n={}){let t=this.resolveStatusUrl(e,n),o=await this.fetchJson(t,{method:"GET",headers:this.apiKeyHeaders()});if(o.status===404)return{status:"not_found"};if(!o.ok)throw Error(`Status check failed: ${o.status} ${await o.text()}`);return await o.json()}async waitForAsyncStatus(e,n={}){let t=Date.now()+(n.timeoutMs??180000),o=n.intervalMs??2000;while(Date.now()<t){if(n.signal?.aborted)throw Error("Async status polling aborted.");let r=await this.getAsyncStatus(e,n);if(r.status==="awaiting_approval"||r.status==="completed"||r.status==="failed"||r.status==="expired"||r.status==="not_found")return r;await ye(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(me(e))});if(n.status!==201)throw Error(`Create cron job failed: ${n.status} ${await p(n)}`);return await n.json()}async listCrons(){let e=await this.fetchJson(`${this.baseUrl}/v1/crons`,{method:"GET",headers:this.apiKeyHeaders()});if(!e.ok)throw Error(`List cron jobs failed: ${e.status} ${await p(e)}`);return(await e.json()).crons}async getCron(e){let n=await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(e)}`,{method:"GET",headers:this.apiKeyHeaders()});if(n.status===404)return null;if(!n.ok)throw Error(`Get cron job failed: ${n.status} ${await p(n)}`);return await n.json()}async listCronRuns(e,n={}){let t=new URLSearchParams;if(n.limit!==void 0)t.set("limit",String(n.limit));let o=t.size>0?`?${t}`:"",r=await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(e)}/runs${o}`,{method:"GET",headers:this.apiKeyHeaders()});if(!r.ok)throw Error(`List cron job runs failed: ${r.status} ${await p(r)}`);return(await r.json()).runs}async updateCron(e,n){let t=await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(e)}`,{method:"PATCH",headers:this.apiKeyHeaders(),body:JSON.stringify(n)});if(!t.ok)throw Error(`Update cron job failed: ${t.status} ${await p(t)}`);return await t.json()}async deleteCron(e){let n=await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(e)}`,{method:"DELETE",headers:this.apiKeyHeaders()});if(n.status===404)return!1;if(!n.ok)throw Error(`Delete cron job failed: ${n.status} ${await p(n)}`);return(await n.json()).deleted}scopedUrl(e,n=""){if(e.projectSlug&&e.stageSlug&&e.endpointId)return`${this.baseUrl}/v1/${encodeURIComponent(e.projectSlug)}/agents/${encodeURIComponent(e.stageSlug)}/${encodeURIComponent(e.endpointId)}${n}`;return`${this.baseUrl}${n}`}async openStream(e,n){if(this.apiKey)return await this.fetchCore(e,n,{Authorization:`Bearer ${this.apiKey}`});throw Error(`BroodsClient streams directly from the core service at ${this.baseUrl}. Provide apiKey. For a self-hosted core service, set host/baseUrl or BROODS_HOST/BROODS_BASE_URL.`)}async fetchCore(e,n,t){try{return await this.fetchImpl(n,{method:"POST",headers:{"Content-Type":"application/json",Accept:"text/event-stream",...t},body:JSON.stringify(e)})}catch(o){throw Error(`Cannot access the broods core service at ${n}. The SDK uses ${x} by default; set host/baseUrl or BROODS_HOST/BROODS_BASE_URL to your own core service URL if your account uses a custom deployment. Cause: ${o instanceof Error?o.message:String(o)}`)}}apiKeyHeaders(){if(!this.apiKey)throw Error("BroodsClient requires apiKey or BROODS_API_KEY.");return{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`}}async fetchJson(e,n){try{return await this.fetchImpl(e,{...n,headers:{"Content-Type":"application/json",...n.headers}})}catch(t){throw Error(`Cannot access the broods core service at ${e}. Cause: ${t instanceof Error?t.message:String(t)}`)}}resolveStatusUrl(e,n){if(typeof e!=="string")return e.statusUrl;if(/^https?:\/\//.test(e))return e;if(!n.agentId)throw Error("Polling by status id requires agentId.");return pe(this.baseUrl,e,n.agentId)}}function A(e){let n=e.trim(),t=/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(n)?n:`https://${n}`;return d(t)}function K(e,n){let t=e.eventId??`${n}-${Date.now()}`;return{agentId:e.agentId,eventId:t,conversationKey:e.conversationKey??t,events:k(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 H(e,n){if(!e||typeof e!=="object")throw Error("Async response must be an object");let t=e.statusUrl;if(typeof t!=="string"||t.length===0)throw Error("Async response missing statusUrl");let o=ge(t);if(!o.statusId)throw Error("Async response statusUrl missing status id");let r=typeof e.eventId==="string"?e.eventId:o.statusId,s=e.status,i=e.requestedMode;return{statusUrl:t,statusId:o.statusId,eventId:r,agentId:o.agentId??n.agentId,...s==="accepted"||s==="queued"||s==="applied"||s==="processing"?{status:s}:{},...i==="reject"||i==="followup"||i==="collect"||i==="steer"?{requestedMode:i}:{}}}function ge(e){let n=new URL(e),t=n.pathname.match(/\/status\/([^/]+)$/);return{statusId:t?.[1]?decodeURIComponent(t[1]):void 0,agentId:n.searchParams.get("agentId")??void 0}}function pe(e,n,t){return`${A(e)}/status/${encodeURIComponent(n)}?agentId=${encodeURIComponent(t)}`}async function fe(e,n){if((e.headers.get("content-type")??"").toLowerCase().includes("text/event-stream"))return await e.body?.cancel().catch(()=>{}),`expected ${n}, but the server returned an SSE stream. This usually means the core deployment routed /async to the direct streaming runner instead of the async handler.`;let o=await e.text();if(o.length<=2000)return o;return`${o.slice(0,2000)}... [truncated ${o.length-2000} chars]`}async function p(e){let n=await e.text();if(n.includes("Request body must include eventId and conversationKey"))return`${n}. Cron job APIs must be served by the configured baseUrl. Prefer defining stable cron jobs with defineCron(...) in broods/ and syncing with \`broods dev\` or \`broods deploy\`.`;return n}function me(e){if("agentId"in e)return e;let n=e.agent,t=typeof n==="string"?n:n.id,{agent:o,...r}=e;return{...r,agentId:t}}function ye(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 he(e){if(typeof e==="string")return e;if(!e||typeof e!=="object")return String(e);let n=e,t=n.data?.error?.message??n.message??n.responseBody??JSON.stringify(e),o=n.name?`${n.name}: `:"",r=n.statusCode?` (HTTP ${n.statusCode})`:"";return`${o}${t}${r}`}var Ce=2000,j=0,C=1;class Se{baseUrl;apiKey;WebSocketImpl;connectTimeoutMs;constructor(e={}){if(I(),this.baseUrl=F(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||x),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??Ce}subscribe(e,n={}){let t=this.resolveWebSocket(),o=this.buildUrl(e),r=Re(e),s=L(this.baseUrl),i=new t(o),a=!1,u=!1,g=setTimeout(()=>{if(!a)y(s)},this.connectTimeoutMs),w=(c=1000,R="client closed")=>{if(u)return;if(u=!0,clearTimeout(g),e.signal?.removeEventListener("abort",S),i.readyState===C||i.readyState===j)i.close(c,R)},y=(c)=>{n.onError?.(c),w(1011,"websocket error")},X=()=>{n.onDone?.(),w(1000,"done")},S=()=>{if(i.readyState===C)i.send(JSON.stringify({type:"cancel"}));y(Error("WebSocket run aborted."))};return e.signal?.addEventListener("abort",S,{once:!0}),i.onopen=()=>{if(a=!0,clearTimeout(g),e.signal?.aborted){S();return}i.send(JSON.stringify({type:"execute",agentId:r,events:k(e),sessionId:e.sessionId,...e.eventId!==void 0?{eventId:e.eventId}:{},...e.mode!==void 0?{mode:e.mode}:{},...e.idempotencyKey!==void 0?{idempotencyKey:e.idempotencyKey}:{},...e.system!==void 0?{system:e.system}:{},...e.model!==void 0?{model:e.model}:{}}))},i.onmessage=(c)=>{if(typeof c.data!=="string")return;let R=q(c.data);if(!R)return;let b=G(R,n);switch(n.onMessage?.(b),b.type){case"meta":n.onMeta?.(b);break;case"done":X();break;case"error":y(Error(J(b.error)));break}},i.onerror=()=>{y(s)},i.onclose=(c)=>{if(clearTimeout(g),e.signal?.removeEventListener("abort",S),u||c.code===1000)return;y(Error(c.reason||"WebSocket connection closed."))},{url:o,sendControl:function(c){if(i.readyState!==C)throw Error("WebSocket is not open");i.send(JSON.stringify({type:"control",...c}))},close:w}}attach(e,n={}){let t=this.resolveWebSocket(),o=this.buildUrl(e),r=new t(o),s=!1,i=(a=1000,u="client closed")=>{if(s)return;if(s=!0,r.readyState===C||r.readyState===j)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 g=G(u,n);if(n.onMessage?.(g),g.type==="done")n.onDone?.();if(g.type==="error")n.onError?.(Error(J(g.error)))},r.onerror=()=>n.onError?.(L(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!==C)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=be(e),t=e.projectSlug??e.agent?.projectSlug,o=e.stageSlug??e.agent?.stageSlug,r=t?`/${t}`:"",s=o?`/${o}`:"";return`${ke(this.baseUrl)}/v1${r}/agents${s}/${encodeURIComponent(n)}/ws?token=${encodeURIComponent(this.apiKey)}`}resolveWebSocket(){let e=this.WebSocketImpl??globalThis.WebSocket;if(!e)throw Error("WebSocket is not available. Pass a WebSocket implementation in BroodsWebSocketClient options.");return e}}function Re(e){let n=e.agentId??e.agent?.id??e.endpointId;if(!n)throw Error("WebSocket run requires agent, agentId, or endpointId.");return n}function be(e){let n=e.endpointId??e.agent?.endpointId??e.agent?.id;if(!n)throw Error("WebSocket run requires agent or endpointId.");return n}function ke(e){let n=new URL(F(e));if(n.protocol==="https:")n.protocol="wss:";if(n.protocol==="http:")n.protocol="ws:";return d(n.toString())}function F(e){let n=e.trim();if(/^wss?:\/\//.test(n))return d(n);return A(n)}function L(e){return Error(`Cannot access the WebSocket service at ${e}.`)}function G(e,n){if(e.type!=="output")return e;let t=e;return n.onOutput?.(t),t.data}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 J(e){if(typeof e==="string")return e;if(e&&typeof e==="object"&&typeof e.message==="string")return e.message;return e===void 0?"WebSocket stream error.":JSON.stringify(e)}var z=Symbol.for("broods.resource"),V=Symbol.for("broods.config"),Y=Symbol.for("broods.connection"),Z=/^[A-Z][A-Z0-9_]{0,63}$/,ze=new Proxy((e)=>{if(!Z.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"&&Z.test(n))throw Error(`env.${n} is not supported; use env("${n}")`);return Reflect.get(e,n,t)}});function l(e,n,t,o){return{[z]:!0,kind:e,name:n,...t?{description:t}:{},config:o}}function f(e,n){let{partition:t,...o}=n;return{[Y]:!0,kind:"connection",type:e,...t?{partition:t}:{},config:o}}function m(e,n,t,o,r){return l("channelRecord",e,n,{...r,externalId:t,...o?{workspaceRef:o}:{}})}function Ve(e){return f("discord",e)}function Ye(e){return f("github",e)}function Xe(e){return f("pancake",e)}function Qe(e){return f("slack",e)}function en(e){return f("telegram",e)}function nn(e){return f("zalo",e)}function tn(e){let{name:n,description:t,channelId:o,guildId:r,...s}=e;return m(n,t,o,r,s)}function on(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 m(n,t,o,s,r)}function rn(e){let{name:n,description:t,conversationId:o,...r}=e;return m(n,t,o,void 0,r)}function sn(e){let{name:n,description:t,channelId:o,teamId:r,...s}=e;return m(n,t,o,r,s)}function an(e){let{name:n,description:t,chatId:o,...r}=e;return m(n,t,o,void 0,r)}function cn(e){let{name:n,description:t,chatId:o,...r}=e;return m(n,t,o,void 0,r)}function un(e){return{[V]:!0,config:e}}function dn(e){let{name:n,description:t,...o}=e;return l("agent",n,t,o)}function ln(e){return e}function gn(e){let{name:n,description:t,...o}=e;return l("workspace",n,t,o)}function pn(e){let{name:n,description:t,...o}=e;return l("sandbox",n,t,o)}function fn(e){let{name:n,description:t,...o}=e;return l("skill",n,t,o)}function mn(e){let{name:n,...t}=e;return l("tool",n,void 0,t)}function yn(e){let{name:n,description:t,...o}=e;return l("mcp",n,t,o)}function hn(e){let{name:n,description:t,...o}=e;return l("policy",n,t,o)}function Cn(e){let{name:n,description:t,...o}=e;return l("cron",n,t,o)}function Sn(e){return Boolean(e&&typeof e==="object"&&e[z])}function Rn(e){return Boolean(e&&typeof e==="object"&&e[Y])}function bn(e){return Boolean(e&&typeof e==="object"&&e[V])}
5
+ export{k as d,D as e,Ee as f,De as g,h,Ne as i,Te as j,O as k,d as l,Oe as m,I as n,$e as o,x as p,B as q,le as r,A as s,Se as t,ke as u,ze as v,Ve as w,Ye as x,Xe as y,Qe as z,en as A,nn as B,tn as C,on as D,rn as E,sn as F,an as G,cn as H,un as I,dn as J,ln as K,gn as L,pn as M,fn as N,mn as O,yn as P,hn as Q,Cn as R,Sn as S,Rn as T,bn as U};