broods 0.44.0 → 0.45.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/README.md CHANGED
@@ -59,7 +59,7 @@ const result = await client.run(api.agents.myAgent, {
59
59
  console.log(result.text);
60
60
  ```
61
61
 
62
- Runtime calls use a stage runtime API key. After `broods deploy`, the CLI
62
+ Runtime calls use a runtime key. After `broods deploy`, the CLI
63
63
  writes `BROODS_API_KEY` to `.env.local`; the SDK also accepts `apiKey`,
64
64
  `BROODS_API_KEY`, `baseUrl`, and `BROODS_BASE_URL`.
65
65
 
@@ -73,11 +73,11 @@ your code.
73
73
 
74
74
  **Dynamic config at runtime (`BroodsAccountClient`).** A multi-tenant product
75
75
  that provisions one agent per customer creates and mutates config while it runs.
76
- For that, use the account config client with your account secret. It is the
76
+ For that, use the account config client with your account key. It is the
77
77
  complete typed client for the account config plane: agents, sandboxes (config +
78
78
  suspend/resume/terminate/snapshot/terminal), workspaces (config + file
79
79
  upload/rename/delete/download), tools, policies, skills, crons (+ run history),
80
- and the account itself (metadata, secret rotation, deletion). It is a separate,
80
+ and the account itself (metadata, key rotation, deletion). It is a separate,
81
81
  dependency-free entry point (`broods/account`) built on plain `fetch`, so it
82
82
  also works in edge runtimes such as Convex actions and Cloudflare Workers where
83
83
  the main SDK entry (which reads `.env` files from disk) cannot load:
@@ -86,7 +86,7 @@ the main SDK entry (which reads `.env` files from disk) cannot load:
86
86
  import { BroodsAccountClient, envPlaceholder } from "broods/account";
87
87
 
88
88
  // baseUrl defaults to https://gateway.broods.app (override with BROODS_BASE_URL);
89
- // the secret falls back to BROODS_ACCOUNT_SECRET from the runtime's environment.
89
+ // the account key falls back to BROODS_ACCOUNT_SECRET from the runtime's environment.
90
90
  const account = new BroodsAccountClient({
91
91
  accountSecret: process.env.BROODS_ACCOUNT_SECRET,
92
92
  });
@@ -101,7 +101,7 @@ const created = await account.createAgent({
101
101
  },
102
102
  });
103
103
 
104
- // Store an account secret once, then reference it from any dynamic agent.
104
+ // Store an account-wide variable once, then reference it from any dynamic agent.
105
105
  // Reads list only names/timestamps; secret values are never returned.
106
106
  await account.setEnvVar("OVH_API_KEY", process.env.OVH_API_KEY!);
107
107
  await account.updateAgent(created.agentId, {
@@ -142,7 +142,7 @@ const runs = await account.listCronRuns("cron_1", { limit: 20 });
142
142
  // Persistent sandbox lifecycle is driven by reservationKey.
143
143
  await account.suspendSandbox(sandbox.sandboxId, "ws-namespace");
144
144
 
145
- // Rotate the account secret when needed (the returned secret is shown once).
145
+ // Rotate the account key when needed (the new key is returned once, as `secret`).
146
146
  const { secret } = await account.rotateSecret();
147
147
  ```
148
148
 
package/dist/account.d.ts CHANGED
@@ -178,10 +178,11 @@ interface McpOauth {
178
178
  }
179
179
 
180
180
  /**
181
- * Agent configuration: types for the per-agent settings object, the runtime
182
- * projection of a stored config, and encryption helpers. The validation rules
183
- * are the config plane's (`@broods/convex/model/agentRules`), so a config is
184
- * judged the same on write and on every run.
181
+ * Agent configuration: types for the per-agent settings object and the
182
+ * runtime projection of a stored config. The validation rules are the config
183
+ * plane's (`@broods/convex/model/agentRules`), so a config is judged the same
184
+ * on write and on every run. Decrypting the stored blob is
185
+ * `../convex/account-keys.ts`.
185
186
  * Account types and auth live in `./accounts.ts` and `../auth.ts`.
186
187
  */
187
188
 
@@ -715,17 +716,24 @@ type UpdateCronInput = {
715
716
  input?: never;
716
717
  });
717
718
 
719
+ /**
720
+ * The sandbox provider list: the one list the schema validator, the config
721
+ * rules, core's executor registry and the SDK derive from. A leaf on purpose,
722
+ * so `schema.ts` can read it without pulling the rules' import graph.
723
+ */
724
+ declare const SANDBOX_PROVIDERS: readonly ["sandbox", "lambda", "e2b", "daytona", "vercel", "machine", "custom"];
725
+ type SandboxProvider = (typeof SANDBOX_PROVIDERS)[number];
726
+
718
727
  /**
719
728
  * Sandbox-config validation for the Convex config plane. Ports core's former
720
729
  * storage/sandbox-config.ts normalizer so the public /v1/sandboxes contract
721
730
  * is unchanged. The public projection lives in ./responses.ts.
722
731
  */
723
- declare const SANDBOX_PROVIDERS: readonly ["sandbox", "lambda", "e2b", "daytona", "vercel", "machine"];
732
+
724
733
  declare const SANDBOX_RUNTIMES: readonly ["bash", "python", "node"];
725
734
  declare const SANDBOX_PERMISSION_MODES: readonly ["edit", "ask", "bypass"];
726
735
  declare const SANDBOX_NETWORK_MODES: readonly ["allow-all", "deny-all", "restricted"];
727
736
  declare const SANDBOX_SIZE_NAMES: readonly ["tiny", "xsmall", "small", "medium", "large"];
728
- type SandboxProvider = (typeof SANDBOX_PROVIDERS)[number];
729
737
  type RuntimeName = (typeof SANDBOX_RUNTIMES)[number];
730
738
  type PermissionMode = (typeof SANDBOX_PERMISSION_MODES)[number];
731
739
  type NetworkMode = (typeof SANDBOX_NETWORK_MODES)[number];
@@ -764,6 +772,29 @@ interface SandboxConfig {
764
772
  options?: Record<string, unknown>;
765
773
  }
766
774
 
775
+ /**
776
+ * Workspace isolation levels, the one list the config plane, core, the
777
+ * dashboard and the CLI read. No imports on purpose: the CLI bundles this file
778
+ * without the Convex runtime behind workspaceRules (like modelProviders.ts).
779
+ */
780
+ declare const WORKSPACE_ISOLATION_LEVELS: readonly ["conversation", "agent"];
781
+ /**
782
+ * How a workspace splits its files. "conversation" mounts a folder per channel
783
+ * partition, "agent" gives every attached agent its own folder. Unset shares
784
+ * one root between every agent and conversation.
785
+ */
786
+ type WorkspaceIsolation = (typeof WORKSPACE_ISOLATION_LEVELS)[number];
787
+
788
+ /**
789
+ * Workspace-config validation for the Convex config plane. Ports core's
790
+ * former storage/workspace-config.ts normalizer so the public /v1/workspaces
791
+ * contract is unchanged. Workspace config holds no secrets (a roleArn is not
792
+ * a secret), so it is stored and returned in plaintext. Pure module, safe
793
+ * for the default Convex runtime. The public projection lives in
794
+ * ./responses.ts. Core runs the storage access rule too, so it reads the env
795
+ * names of both sides.
796
+ */
797
+
767
798
  declare const WORKSPACE_STORAGE_PROVIDERS: readonly ["s3"];
768
799
  type WorkspaceStorageProvider = (typeof WORKSPACE_STORAGE_PROVIDERS)[number];
769
800
  type WorkspaceStorageAuth = {
@@ -781,9 +812,13 @@ interface WorkspaceStorageConfig {
781
812
  prefix?: string;
782
813
  auth?: WorkspaceStorageAuth;
783
814
  }
815
+ /** What the API accepts: the boolean spelling of isolation is stored as its level. */
816
+ type WorkspaceConfigInput = Omit<WorkspaceConfig, "isolation"> & {
817
+ isolation?: WorkspaceIsolation | boolean;
818
+ };
784
819
  interface WorkspaceConfig {
785
820
  storage: WorkspaceStorageConfig;
786
- isolation?: boolean;
821
+ isolation?: WorkspaceIsolation;
787
822
  harness?: {
788
823
  workspace?: {
789
824
  enabled?: boolean;
@@ -805,7 +840,7 @@ declare const AGENT_POLICY_ACTIONS: readonly ["agent.invoke", "tool.call", "work
805
840
  * resource route. Roles carry the same PolicyDocument shape as agent policies;
806
841
  * each caller passes its action set to `normalizePolicyDocument`.
807
842
  */
808
- 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", "workspaces:read", "workspaces:write"];
843
+ declare const API_POLICY_ACTIONS: readonly ["account:read", "account:write", "agents:read", "agents:write", "audit:read", "audit: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", "workspaces:read", "workspaces:write"];
809
844
  type AgentPolicyAction = (typeof AGENT_POLICY_ACTIONS)[number];
810
845
  type ApiPolicyAction = (typeof API_POLICY_ACTIONS)[number];
811
846
  type PolicyAction = AgentPolicyAction | ApiPolicyAction;
@@ -980,7 +1015,7 @@ interface Connection {
980
1015
  * server runtimes, as well as Node and Bun.
981
1016
  *
982
1017
  * Auth: every call sends `Authorization: Bearer {accountSecret}` to
983
- * `{baseUrl}/v1/...`, or a short-lived `fp_sts_` role session token from
1018
+ * `{baseUrl}/v1/...`, or a short-lived `bsts_` role session token from
984
1019
  * `assumeRole()`, limited to what the role's policy allows. Secrets inside
985
1020
  * agent configs are encrypted at rest by the platform and come back redacted
986
1021
  * (`********`) on reads.
@@ -993,11 +1028,11 @@ type ConfigPatch<T> = T extends readonly unknown[] ? T : T extends object ? {
993
1028
  interface BroodsAccountClientOptions {
994
1029
  /** Base URL of the broods gateway. Falls back to `BROODS_BASE_URL`, then `https://gateway.broods.app`. */
995
1030
  baseUrl?: string;
996
- /** Account secret used as the Bearer token. Falls back to `BROODS_ACCOUNT_SECRET`. */
1031
+ /** Account key used as the Bearer token. Falls back to `BROODS_ACCOUNT_SECRET`. */
997
1032
  accountSecret?: string;
998
1033
  /**
999
- * Short-lived `fp_sts_` role session token (from {@link BroodsAccountClient.assumeRole})
1000
- * used as the Bearer instead of the account secret. The session can only do
1034
+ * Short-lived `bsts_` role session token (from {@link BroodsAccountClient.assumeRole})
1035
+ * used as the Bearer instead of the account key. The session can only do
1001
1036
  * what its role's policy allows. Falls back to `BROODS_SESSION_TOKEN`.
1002
1037
  */
1003
1038
  sessionToken?: string;
@@ -1008,6 +1043,8 @@ interface BroodsAccount {
1008
1043
  accountId: string;
1009
1044
  username: string;
1010
1045
  status: string;
1046
+ /** Days an audit row is kept; absent means the 90 day default. */
1047
+ auditRetentionDays?: number;
1011
1048
  [key: string]: unknown;
1012
1049
  }
1013
1050
  /** Public agent record; `config` comes back with secret values redacted. */
@@ -1083,7 +1120,7 @@ interface AccountPolicy {
1083
1120
  /**
1084
1121
  * Public account-role record returned by the roles routes. The policy uses the
1085
1122
  * API action namespace (`"agents:read"`, `"crons:write"`, ...); `projectId` and
1086
- * `stageId` bound which stage runtime keys may assume the role.
1123
+ * `stageId` bound which runtime keys may assume the role.
1087
1124
  */
1088
1125
  interface AccountRole {
1089
1126
  accountId: string;
@@ -1096,9 +1133,82 @@ interface AccountRole {
1096
1133
  createdAt: string;
1097
1134
  updatedAt: string;
1098
1135
  }
1136
+ /** One link of an agent actor's delegation chain: who asked, or an agent that delegated. */
1137
+ type AuditPrincipalLink = {
1138
+ kind: "user";
1139
+ id: string;
1140
+ name?: string;
1141
+ channel?: string;
1142
+ } | {
1143
+ kind: "api";
1144
+ keyKind: "account" | "deployment" | "cron";
1145
+ } | {
1146
+ kind: "agent";
1147
+ agentId: string;
1148
+ };
1149
+ /**
1150
+ * One row of the account's hash-chained audit ledger, as `GET /v1/audit`
1151
+ * serves it. `hash` is sha256 over the canonical JSON of every field but
1152
+ * `hash` itself (an absent optional field is left out, not null), with
1153
+ * `prevHash` linking it to the row before.
1154
+ */
1155
+ interface AuditEvent {
1156
+ accountId: string;
1157
+ seq: number;
1158
+ prevHash: string;
1159
+ hash: string;
1160
+ /** Unix ms. */
1161
+ at: number;
1162
+ actor: {
1163
+ kind: string;
1164
+ id?: string;
1165
+ email?: string;
1166
+ name?: string;
1167
+ agentId?: string;
1168
+ /** On an agent actor: who asked, then each agent that delegated, oldest first. */
1169
+ chain?: AuditPrincipalLink[];
1170
+ };
1171
+ action: string;
1172
+ resource: {
1173
+ kind: string;
1174
+ id?: string;
1175
+ name?: string;
1176
+ };
1177
+ summary: string;
1178
+ detailsJson?: string;
1179
+ projectId?: string;
1180
+ stageId?: string;
1181
+ traceId?: string;
1182
+ }
1183
+ /** A page of ledger rows plus the chain head to compare the last row against. */
1184
+ interface AuditPage {
1185
+ events: AuditEvent[];
1186
+ /** Pass as `since` to read the rows after this page. */
1187
+ nextSince: number;
1188
+ head: {
1189
+ seq: number;
1190
+ hash: string;
1191
+ } | null;
1192
+ }
1193
+ /** Result of `GET /v1/audit/verify`: the chain recomputed over the checked range. */
1194
+ interface AuditVerification {
1195
+ ok: boolean;
1196
+ brokenAtSeq?: number;
1197
+ checkedFrom?: number;
1198
+ checkedTo?: number;
1199
+ }
1200
+ /** The account's audit export target. The signing secret never comes back. */
1201
+ interface AuditSink {
1202
+ kind: "webhook";
1203
+ url: string;
1204
+ /** Highest seq the receiver acknowledged. */
1205
+ exportedSeq: number;
1206
+ lastError?: string;
1207
+ updatedAt: string;
1208
+ }
1099
1209
  /** Short-lived role session minted by `POST /v1/account/assume-role`. */
1100
1210
  interface AssumeRoleResult {
1101
- /** `fp_sts_` bearer token; pass it as `sessionToken` to a new client. */
1211
+ /** `bsts_` bearer token; pass it as `sessionToken` to a new client. */
1102
1212
  token: string;
1103
1213
  /** ISO timestamp when the session stops working. */
1104
1214
  expiresAt: string;
@@ -1245,7 +1355,7 @@ interface SkillUploadInput {
1245
1355
  }>;
1246
1356
  url?: string;
1247
1357
  }
1248
- /** Result of `POST /v1/account/rotate-secret`. The returned `secret` is shown once; the old secret stops working immediately. */
1358
+ /** Result of `POST /v1/account/rotate-secret`. The returned `secret` is the new account key, shown once; the old key stops working immediately. */
1249
1359
  interface RotateSecretResult {
1250
1360
  account: BroodsAccount;
1251
1361
  secret: string;
@@ -1281,7 +1391,7 @@ declare class BroodsAccountApiError extends Error {
1281
1391
  declare function envPlaceholder(name: string): string;
1282
1392
  /**
1283
1393
  * The credential the environment supplies, with a role session winning over
1284
- * the account secret. The constructor throws through this same resolution, so
1394
+ * the account key. The constructor throws through this same resolution, so
1285
1395
  * callers that can run without an account credential (`broods mcp` with only a
1286
1396
  * stored login) probe here instead of catching the constructor.
1287
1397
  */
@@ -1303,17 +1413,19 @@ declare class BroodsAccountClient {
1303
1413
  updateAccount(patch: {
1304
1414
  username?: string;
1305
1415
  description?: string | null;
1416
+ /** 1 to 3650 days, or null to return to the 90 day default. */
1417
+ auditRetentionDays?: number | null;
1306
1418
  }): Promise<BroodsAccount | null>;
1307
1419
  /**
1308
- * Exchange a role for a short-lived `fp_sts_` session token. Callable with
1309
- * the account secret, a CLI login token, or a stage runtime key (the latter
1420
+ * Exchange a role for a short-lived `bsts_` session token. Callable with
1421
+ * the account key, a CLI login token, or a runtime key (the latter
1310
1422
  * only into roles scoped to the key's own project/stage). Construct a new
1311
1423
  * client with `{ sessionToken: result.token }` to act as the role.
1312
1424
  */
1313
1425
  assumeRole(roleId: string, options?: {
1314
1426
  ttlSeconds?: number;
1315
1427
  }): Promise<AssumeRoleResult>;
1316
- /** Rotate the account secret. The returned `secret` is shown once and the current secret stops working immediately, so persist it before the process exits. */
1428
+ /** Rotate the account key. The returned `secret` is the new key, shown once, and the current key stops working immediately, so persist it before the process exits. */
1317
1429
  rotateSecret(): Promise<RotateSecretResult>;
1318
1430
  /** Delete this account and cascade-clean every account-scoped resource. `cleanup` reports per-resource deletion counts. */
1319
1431
  deleteAccount(): Promise<DeleteAccountResult>;
@@ -1364,13 +1476,13 @@ declare class BroodsAccountClient {
1364
1476
  createWorkspace(input: {
1365
1477
  name: string;
1366
1478
  description?: string;
1367
- config?: WorkspaceConfig;
1479
+ config?: WorkspaceConfigInput;
1368
1480
  }): Promise<AccountWorkspace>;
1369
1481
  getWorkspace(workspaceId: string): Promise<AccountWorkspace | null>;
1370
1482
  updateWorkspace(workspaceId: string, patch: {
1371
1483
  name?: string;
1372
1484
  description?: string | null;
1373
- config?: ConfigPatch<WorkspaceConfig>;
1485
+ config?: ConfigPatch<WorkspaceConfigInput>;
1374
1486
  }): Promise<AccountWorkspace | null>;
1375
1487
  deleteWorkspace(workspaceId: string): Promise<boolean>;
1376
1488
  /** Flat listing of every file in the workspace's S3-backed filesystem. Returns [] when the workspace is gone. */
@@ -1457,6 +1569,32 @@ declare class BroodsAccountClient {
1457
1569
  status?: "active" | "disabled";
1458
1570
  }): Promise<AccountRole | null>;
1459
1571
  deleteRole(roleId: string): Promise<boolean>;
1572
+ /** The audit ledger: rows since a seq, chain verification, and the webhook sink. */
1573
+ readonly audit: {
1574
+ /** Rows with `seq > since`, oldest first, at most `limit` (default 100, max 500). */
1575
+ list: (options?: {
1576
+ since?: number;
1577
+ limit?: number;
1578
+ }) => Promise<AuditPage>;
1579
+ /**
1580
+ * Recompute the chain over `[fromSeq, toSeq]`, from the oldest kept row by
1581
+ * default, at most 1000 rows per call. `ok` covers only `checkedFrom` to
1582
+ * `checkedTo`; on a longer ledger call again with `fromSeq: checkedTo + 1`.
1583
+ */
1584
+ verify: (options?: {
1585
+ fromSeq?: number;
1586
+ toSeq?: number;
1587
+ }) => Promise<AuditVerification>;
1588
+ /** The account's sink, or null when none is set. */
1589
+ getSink: () => Promise<AuditSink | null>;
1590
+ /** Set the one webhook sink. `url` must be public https; `secret` signs each batch. */
1591
+ setSink: (input: {
1592
+ url: string;
1593
+ secret: string;
1594
+ }) => Promise<AuditSink>;
1595
+ /** Remove the sink; exports stop and rows age out on retention alone. Returns whether one existed. */
1596
+ deleteSink: () => Promise<boolean>;
1597
+ };
1460
1598
  listChannels(): Promise<AccountChannel[]>;
1461
1599
  /** Bind one real chat channel to an agent. One active record per place. */
1462
1600
  createChannel(input: {
@@ -1491,4 +1629,4 @@ declare class BroodsAccountClient {
1491
1629
  }
1492
1630
 
1493
1631
  export { BroodsAccountApiError, BroodsAccountClient, envPlaceholder, resolveEnvCredential };
1494
- export type { AccountAgent, AccountChannel, AccountEnvVar, AccountMcp, AccountPolicy, AccountRole, AccountSandbox, AccountWorkspace, AssumeRoleResult, BroodsAccount, BroodsAccountClientOptions, ChannelRecordConfig, ConfigPatch, Connection, ConnectionCode, ConnectionStart, ConnectionStartResult, ConnectionType, CreateAgentResult, CreateMcpInput, DeleteAccountResult, McpOauthInput, RotateSecretResult, SandboxLifecycleResult, SandboxSnapshotResult, SandboxTerminalTicket, SkillUploadInput, StageScope, UpdateAgentInput, UpdateMcpInput, WorkspaceFileEntry };
1632
+ export type { AccountAgent, AccountChannel, AccountEnvVar, AccountMcp, AccountPolicy, AccountRole, AccountSandbox, AccountWorkspace, AssumeRoleResult, AuditEvent, AuditPage, AuditPrincipalLink, AuditSink, AuditVerification, BroodsAccount, BroodsAccountClientOptions, ChannelRecordConfig, ConfigPatch, Connection, ConnectionCode, ConnectionStart, ConnectionStartResult, ConnectionType, CreateAgentResult, CreateMcpInput, DeleteAccountResult, McpOauthInput, RotateSecretResult, SandboxLifecycleResult, SandboxSnapshotResult, SandboxTerminalTicket, SkillUploadInput, StageScope, UpdateAgentInput, UpdateMcpInput, WorkspaceFileEntry };
package/dist/account.js CHANGED
@@ -1 +1 @@
1
- import{m,Ae,Z,R}from"./chunk-a3f2xzbr.js";export{m as BroodsAccountApiError,R as BroodsAccountClient,Ae as envPlaceholder,Z as resolveEnvCredential};
1
+ import{m,Ae,Z,R}from"./chunk-bb3jfhv3.js";export{m as BroodsAccountApiError,R as BroodsAccountClient,Ae as envPlaceholder,Z as resolveEnvCredential};
@@ -1,4 +1,4 @@
1
- import{U,z}from"./chunk-t0fvmbkd.js";import{e,o,a,l,n,t,d,C,s,p,r}from"./chunk-naxtmb6t.js";import{se,fe,D}from"./cli/index.js";import{spawn as te}from"node:child_process";import{hostname as oe}from"node:os";import{performance as L}from"node:perf_hooks";var N=["cursor_position","double_click","hold_key","key","left_click","left_click_drag","left_mouse_down","left_mouse_up","middle_click","mouse_move","right_click","screenshot","scroll","triple_click","type","wait","zoom"];var _={badFrame:{code:4400,reason:"Malformed frame"},occupied:{code:4423,reason:"Already connected from another daemon"},replaced:{code:4409,reason:"Replaced by a newer connection"},unauthorized:{code:4401,reason:"Unauthorized; run broods login"},unknownSandbox:{code:4404,reason:"No machine sandbox with that name in this account"}};var H="/v1/machines/ws",P=t({action:p(N),coordinate:n(o().int()).length(2).optional().describe("[x, y] in screenshot pixels for mouse actions."),start_coordinate:n(o().int()).length(2).optional().describe("[x, y] where a left_click_drag starts."),text:e().optional().describe("Text for type, the key chord for key and hold_key, or modifiers to hold during a click, drag or scroll."),scroll_direction:p(["down","left","right","up"]).optional(),scroll_amount:o().int().min(1).optional().describe("Scroll clicks, default 3."),duration:o().min(0).max(300).optional().describe("Seconds for wait and hold_key."),region:n(o().int()).length(4).optional().describe("[x0, y0, x1, y1] of the screenshot to zoom into."),repeat:o().int().min(1).max(100).optional().describe("How many times to press the key.")}),B=P.extend({type:r("computer"),id:e()}),W=t({type:r("computer-result"),id:e(),text:e().optional(),image:t({data:e(),mediaType:e()}).optional(),app:e().optional(),error:e().optional()}),G=t({type:r("exec"),id:e(),code:e(),cwd:e().optional(),env:s(e(),e()).optional(),timeoutSeconds:o().positive(),outputLimitBytes:o().positive()}),J=t({type:r("hello"),sandbox:e().min(1),hostname:e().optional(),platform:e().optional(),computer:a().optional(),mcp:n(e()).optional(),instance:e().optional(),force:a().optional()}),K=t({type:r("mcp-call"),id:e(),server:e(),tool:e(),args:s(e(),l())}),X=t({type:r("mcp-list"),id:e(),server:e()}),Y=t({type:r("mcp-result"),id:e(),result:d({content:n(l())}).optional(),error:e().optional()}),q=t({type:r("mcp-tools"),id:e(),tools:n(d({name:e(),inputSchema:s(e(),l())})).optional(),error:e().optional()}),Z=t({type:r("ready"),sandboxId:e()}),Q=t({type:r("result"),id:e(),exitCode:o().nullable(),stdout:e(),stderr:e(),durationMs:o(),timedOut:a(),truncated:a()}),V=C("type",[B,G,K,X,Z]),ue=C("type",[W,J,Y,q,Q]);function T(i){let c=new URL(H,i);return c.protocol=c.protocol==="https:"?"wss:":"ws:",c.toString()}function R(i){return ee(V,i)}function ee(i,c){if(typeof c!=="string")return null;try{let u=i.safeParse(JSON.parse(c));return u.success?u.data:null}catch{return null}}var re=new Set([_.occupied.code,_.replaced.code,_.unauthorized.code,_.unknownSandbox.code]),A=72,k=30000,I=1000;function ne(i,c,u){return new Promise((f)=>{let m=L.now(),h=new E(i.outputLimitBytes),w=new E(i.outputLimitBytes),b=!1,x=!1,M=(y,O)=>{if(x)return;if(x=!0,clearTimeout(v),u?.removeEventListener("abort",g),O)w.append(Buffer.from(`${O}
1
+ import{U,z}from"./chunk-hdcy74kj.js";import{e,o,a,l,n,t,d,C,s,p,r}from"./chunk-n890pdvc.js";import{se,fe,D}from"./cli/index.js";import{spawn as te}from"node:child_process";import{hostname as oe}from"node:os";import{performance as L}from"node:perf_hooks";var N=["cursor_position","double_click","hold_key","key","left_click","left_click_drag","left_mouse_down","left_mouse_up","middle_click","mouse_move","right_click","screenshot","scroll","triple_click","type","wait","zoom"];var _={badFrame:{code:4400,reason:"Malformed frame"},occupied:{code:4423,reason:"Already connected from another daemon"},replaced:{code:4409,reason:"Replaced by a newer connection"},unauthorized:{code:4401,reason:"Unauthorized; run broods login"},unknownSandbox:{code:4404,reason:"No machine sandbox with that name in this account"}};var H="/v1/machines/ws",P=t({action:p(N),coordinate:n(o().int()).length(2).optional().describe("[x, y] in screenshot pixels for mouse actions."),start_coordinate:n(o().int()).length(2).optional().describe("[x, y] where a left_click_drag starts."),text:e().optional().describe("Text for type, the key chord for key and hold_key, or modifiers to hold during a click, drag or scroll."),scroll_direction:p(["down","left","right","up"]).optional(),scroll_amount:o().int().min(1).optional().describe("Scroll clicks, default 3."),duration:o().min(0).max(300).optional().describe("Seconds for wait and hold_key."),region:n(o().int()).length(4).optional().describe("[x0, y0, x1, y1] of the screenshot to zoom into."),repeat:o().int().min(1).max(100).optional().describe("How many times to press the key.")}),B=P.extend({type:r("computer"),id:e()}),W=t({type:r("computer-result"),id:e(),text:e().optional(),image:t({data:e(),mediaType:e()}).optional(),app:e().optional(),error:e().optional()}),G=t({type:r("exec"),id:e(),code:e(),cwd:e().optional(),env:s(e(),e()).optional(),timeoutSeconds:o().positive(),outputLimitBytes:o().positive()}),J=t({type:r("hello"),sandbox:e().min(1),hostname:e().optional(),platform:e().optional(),computer:a().optional(),mcp:n(e()).optional(),instance:e().optional(),force:a().optional()}),K=t({type:r("mcp-call"),id:e(),server:e(),tool:e(),args:s(e(),l())}),X=t({type:r("mcp-list"),id:e(),server:e()}),Y=t({type:r("mcp-result"),id:e(),result:d({content:n(l())}).optional(),error:e().optional()}),q=t({type:r("mcp-tools"),id:e(),tools:n(d({name:e(),inputSchema:s(e(),l())})).optional(),error:e().optional()}),Z=t({type:r("ready"),sandboxId:e()}),Q=t({type:r("result"),id:e(),exitCode:o().nullable(),stdout:e(),stderr:e(),durationMs:o(),timedOut:a(),truncated:a()}),V=C("type",[B,G,K,X,Z]),ue=C("type",[W,J,Y,q,Q]);function T(i){let c=new URL(H,i);return c.protocol=c.protocol==="https:"?"wss:":"ws:",c.toString()}function R(i){return ee(V,i)}function ee(i,c){if(typeof c!=="string")return null;try{let u=i.safeParse(JSON.parse(c));return u.success?u.data:null}catch{return null}}var re=new Set([_.occupied.code,_.replaced.code,_.unauthorized.code,_.unknownSandbox.code]),A=72,k=30000,I=1000;function ne(i,c,u){return new Promise((f)=>{let m=L.now(),h=new E(i.outputLimitBytes),w=new E(i.outputLimitBytes),b=!1,x=!1,M=(y,O)=>{if(x)return;if(x=!0,clearTimeout(v),u?.removeEventListener("abort",g),O)w.append(Buffer.from(`${O}
2
2
  `));f({type:"result",id:i.id,exitCode:y,stdout:h.text(),stderr:w.text(),durationMs:Math.round(L.now()-m),timedOut:b,truncated:h.truncated||w.truncated})},F=te("bash",["-lc",i.code],{cwd:i.cwd??c,env:{...U(),...i.env},detached:!0,stdio:["ignore","pipe","pipe"]}),S=()=>{if(!F.pid)return;try{process.kill(-F.pid,"SIGKILL")}catch{F.kill("SIGKILL")}},v=setTimeout(()=>{b=!0,S()},i.timeoutSeconds*1000),g=()=>{S(),M(null,"stopped: the daemon closed its socket")};if(u?.addEventListener("abort",g,{once:!0}),F.stdout.on("data",(y)=>h.append(y)),F.stderr.on("data",(y)=>w.append(y)),F.on("error",(y)=>M(null,y.message)),F.on("close",(y)=>M(y)),u?.aborted)g()})}async function we(i){let c=fe(),u=crypto.randomUUID(),f=i.mcpFile?await ce(i.mcpFile,i.log):null,m=i.computer?await ae(i.log):null,h=I;try{while(!i.signal.aborted){let w=Date.now(),b;try{b=await i.credential()}catch(M){if(M instanceof D)throw M;i.log(`stage session unavailable (${M instanceof Error?M.message:String(M)}), retrying in ${Math.round(h/1000)}s`),await se(h,i.signal),h=Math.min(h*2,k);continue}let x=await pe(i,b,u,c,m,f);if(i.signal.aborted)return;if(re.has(x.code))throw Error(x.reason||`core closed the socket (${x.code})`);if(Date.now()-w>k)h=I;i.log(`disconnected (${x.reason||x.code}), reconnecting in ${Math.round(h/1000)}s`),await se(h,i.signal),h=Math.min(h*2,k)}}finally{m?.stop(),await f?.stop()}}class E{#o=[];#e;#t=0;truncated=!1;constructor(i){this.#e=i}append(i){if(this.#t>=this.#e){this.truncated=!0;return}let c=this.#e-this.#t;if(i.byteLength>c){this.#o.push(i.subarray(0,c)),this.#t=this.#e,this.truncated=!0;return}this.#o.push(i),this.#t+=i.byteLength}text(){let i=Buffer.concat(this.#o).toString("utf8");return this.truncated?`${i}
3
3
  [output truncated]`:i}}function ie(i){let c=[i.action];if(i.coordinate)c.push(`at ${i.coordinate.join(",")}`);if(i.text!==void 0)c.push(JSON.stringify(j(i.text)));if(i.region)c.push(`region ${i.region.join(",")}`);return c.join(" ")}function j(i){let c=i.trim().split(`
4
- `)[0]??"";return c.length>A?`${c.slice(0,A)}…`:c}async function ae(i){let{startDesktop:c}=await import("./chunk-5tnnqapc.js"),{display:u,driver:f,permissions:m}=await c(!1);if(i(`computer use on, ${u.width}x${u.height} screenshots`),!m.screenRecording||!m.accessibility)i(" screen recording or accessibility is missing, run `broods machine --doctor --request`");return f}async function ce(i,c){let{McpHost:u,readMcpServersFile:f}=await import("./chunk-ee92fs0x.js"),m=new u(f(i),c);return c(`mcp servers from ${i}: ${m.names().join(", ")}`),m}async function le(i,c,u){try{if(i.type==="mcp-list"){let m=await c.listTools(i.server);return u(` mcp ${i.server}: ${m.length} tools`),{type:"mcp-tools",id:i.id,tools:m}}u(` mcp ${i.server}.${i.tool}`);let f=await c.callTool(i.server,i.tool,i.args);return{type:"mcp-result",id:i.id,result:f}}catch(f){let m=f instanceof Error?f.message:String(f);return u(` mcp ${i.server}: ${m}`),{type:i.type==="mcp-call"?"mcp-result":"mcp-tools",id:i.id,error:m}}}function pe(i,c,u,f,m,h){return new Promise((w)=>{let b=new f(T(i.baseUrl),z(c)),x=new AbortController,M=()=>b.close(1000,"daemon stopped"),F=(v)=>{if(b.readyState===b.OPEN)b.send(JSON.stringify(v))},S=(v,g)=>{i.signal.removeEventListener("abort",M),x.abort(),w({code:v,reason:g})};i.signal.addEventListener("abort",M,{once:!0}),b.onopen=()=>F({type:"hello",sandbox:i.sandbox,hostname:oe(),platform:process.platform,computer:m!==null,mcp:h?.names(),instance:u,force:i.force}),b.onmessage=(v)=>{let g=R(v.data);if(g?.type==="ready"){i.log(`connected as ${i.sandbox} (${g.sandboxId})`);return}if(g?.type==="computer"&&m){i.log(` ${ie(g)}`),m.run(g).then((y)=>{if(y.error)i.log(` error: ${y.error}`);F(y)});return}if((g?.type==="mcp-call"||g?.type==="mcp-list")&&h){le(g,h,i.log).then(F);return}if(g?.type!=="exec")return;i.log(`$ ${j(g.code)}`),ne(g,i.cwd,x.signal).then((y)=>{i.log(` exit ${y.exitCode??"none"} in ${y.durationMs}ms${y.timedOut?" (timed out)":""}`),F(y)})},b.onerror=()=>S(1006,"connection failed"),b.onclose=(v)=>S(v.code,v.reason)})}export{we as runMachineDaemon};
4
+ `)[0]??"";return c.length>A?`${c.slice(0,A)}…`:c}async function ae(i){let{startDesktop:c}=await import("./chunk-5tnnqapc.js"),{display:u,driver:f,permissions:m}=await c(!1);if(i(`computer use on, ${u.width}x${u.height} screenshots`),!m.screenRecording||!m.accessibility)i(" screen recording or accessibility is missing, run `broods machine --doctor --request`");return f}async function ce(i,c){let{McpHost:u,readMcpServersFile:f}=await import("./chunk-b3q14j68.js"),m=new u(f(i),c);return c(`mcp servers from ${i}: ${m.names().join(", ")}`),m}async function le(i,c,u){try{if(i.type==="mcp-list"){let m=await c.listTools(i.server);return u(` mcp ${i.server}: ${m.length} tools`),{type:"mcp-tools",id:i.id,tools:m}}u(` mcp ${i.server}.${i.tool}`);let f=await c.callTool(i.server,i.tool,i.args);return{type:"mcp-result",id:i.id,result:f}}catch(f){let m=f instanceof Error?f.message:String(f);return u(` mcp ${i.server}: ${m}`),{type:i.type==="mcp-call"?"mcp-result":"mcp-tools",id:i.id,error:m}}}function pe(i,c,u,f,m,h){return new Promise((w)=>{let b=new f(T(i.baseUrl),z(c)),x=new AbortController,M=()=>b.close(1000,"daemon stopped"),F=(v)=>{if(b.readyState===b.OPEN)b.send(JSON.stringify(v))},S=(v,g)=>{i.signal.removeEventListener("abort",M),x.abort(),w({code:v,reason:g})};i.signal.addEventListener("abort",M,{once:!0}),b.onopen=()=>F({type:"hello",sandbox:i.sandbox,hostname:oe(),platform:process.platform,computer:m!==null,mcp:h?.names(),instance:u,force:i.force}),b.onmessage=(v)=>{let g=R(v.data);if(g?.type==="ready"){i.log(`connected as ${i.sandbox} (${g.sandboxId})`);return}if(g?.type==="computer"&&m){i.log(` ${ie(g)}`),m.run(g).then((y)=>{if(y.error)i.log(` error: ${y.error}`);F(y)});return}if((g?.type==="mcp-call"||g?.type==="mcp-list")&&h){le(g,h,i.log).then(F);return}if(g?.type!=="exec")return;i.log(`$ ${j(g.code)}`),ne(g,i.cwd,x.signal).then((y)=>{i.log(` exit ${y.exitCode??"none"} in ${y.durationMs}ms${y.timedOut?" (timed out)":""}`),F(y)})},b.onerror=()=>S(1006,"connection failed"),b.onclose=(v)=>S(v.code,v.reason)})}export{we as runMachineDaemon};
@@ -1 +1 @@
1
- import{U}from"./chunk-t0fvmbkd.js";import{Oe,e,n,t,s}from"./chunk-naxtmb6t.js";import{readFileSync as m}from"node:fs";import{dirname as d,resolve as p}from"node:path";import{Client as g}from"@modelcontextprotocol/client";import{StdioClientTransport as h}from"@modelcontextprotocol/client/stdio";var f={name:"broods-machine",version:"1.0.0"},u=t({command:e().min(1),args:n(e()).default([]),env:s(e(),e()).optional(),cwd:e().optional()}),w=t({mcpServers:s(e(),u).refine((r)=>Object.keys(r).length>0,"lists no MCP servers")});class y{#t=new Map;#e;#r;constructor(r,o){this.#r=r,this.#e=o}async callTool(r,o,i){return await(await this.#n(r)).callTool({name:o,arguments:i})}async listTools(r){return(await(await this.#n(r)).listTools()).tools}names(){return[...this.#r.keys()]}async stop(){let r=[...this.#t.values()];this.#t.clear(),await Promise.all(r.map((o)=>o.then((i)=>i.close()).catch(()=>{})))}#n(r){let o=this.#r.get(r);if(!o)return Promise.reject(Error(`no MCP server named "${r}" in the --mcp file`));let i=this.#t.get(r);if(i)return i;let c=(async()=>{this.#e(`mcp ${r}: ${[o.command,...o.args].join(" ")}`);let a=new h({...o,env:{...U(),...o.env},stderr:"inherit"});a.onclose=()=>{if(this.#e(`mcp ${r}: exited`),this.#t.get(r)===c)this.#t.delete(r)};let l=new g(f);return await l.connect(a),l})();return c.catch(()=>{if(this.#t.get(r)===c)this.#t.delete(r)}),this.#t.set(r,c),c}}function b(r){let o=p(r),i=w.safeParse(v(o));if(!i.success)throw Error(`${o}: ${Oe(i.error)}`);return new Map(Object.entries(i.data.mcpServers).map(([c,a])=>[c,a.cwd?{...a,cwd:p(d(o),a.cwd)}:a]))}function v(r){try{return JSON.parse(m(r,"utf8"))}catch(o){throw Error(`could not read ${r}: ${o instanceof Error?o.message:String(o)}`)}}export{y as McpHost,b as readMcpServersFile};
1
+ import{U}from"./chunk-hdcy74kj.js";import{Oe,e,n,t,s}from"./chunk-n890pdvc.js";import{readFileSync as m}from"node:fs";import{dirname as d,resolve as p}from"node:path";import{Client as g}from"@modelcontextprotocol/client";import{StdioClientTransport as h}from"@modelcontextprotocol/client/stdio";var f={name:"broods-machine",version:"1.0.0"},u=t({command:e().min(1),args:n(e()).default([]),env:s(e(),e()).optional(),cwd:e().optional()}),w=t({mcpServers:s(e(),u).refine((r)=>Object.keys(r).length>0,"lists no MCP servers")});class y{#t=new Map;#e;#r;constructor(r,o){this.#r=r,this.#e=o}async callTool(r,o,i){return await(await this.#n(r)).callTool({name:o,arguments:i})}async listTools(r){return(await(await this.#n(r)).listTools()).tools}names(){return[...this.#r.keys()]}async stop(){let r=[...this.#t.values()];this.#t.clear(),await Promise.all(r.map((o)=>o.then((i)=>i.close()).catch(()=>{})))}#n(r){let o=this.#r.get(r);if(!o)return Promise.reject(Error(`no MCP server named "${r}" in the --mcp file`));let i=this.#t.get(r);if(i)return i;let c=(async()=>{this.#e(`mcp ${r}: ${[o.command,...o.args].join(" ")}`);let a=new h({...o,env:{...U(),...o.env},stderr:"inherit"});a.onclose=()=>{if(this.#e(`mcp ${r}: exited`),this.#t.get(r)===c)this.#t.delete(r)};let l=new g(f);return await l.connect(a),l})();return c.catch(()=>{if(this.#t.get(r)===c)this.#t.delete(r)}),this.#t.set(r,c),c}}function b(r){let o=p(r),i=w.safeParse(v(o));if(!i.success)throw Error(`${o}: ${Oe(i.error)}`);return new Map(Object.entries(i.data.mcpServers).map(([c,a])=>[c,a.cwd?{...a,cwd:p(d(o),a.cwd)}:a]))}function v(r){try{return JSON.parse(m(r,"utf8"))}catch(o){throw Error(`could not read ${r}: ${o instanceof Error?o.message:String(o)}`)}}export{y as McpHost,b as readMcpServersFile};
@@ -1,2 +1,2 @@
1
- var u=/^[A-Z][A-Z0-9_]*$/;class m 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 Ae(e){if(!u.test(e)||e.length>64)throw Error("envPlaceholder name must match /^[A-Z][A-Z0-9_]*$/ and be at most 64 characters.");return`\${${e}}`}function Z(){return o("BROODS_SESSION_TOKEN")??o("BROODS_ACCOUNT_SECRET")}class R{baseUrl;bearerToken;fetchImpl;constructor(e={}){let t=e.baseUrl??o("BROODS_BASE_URL")??"https://gateway.broods.app",n=e.sessionToken??e.accountSecret??Z();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 m("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 m("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 m("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}/v1/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 m("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 listConnections(){return(await this.request("GET","/v1/account/connections"))?.connections??[]}async getConnection(e){return await this.request("GET",`/v1/account/connections/${e}`)}async startConnection(e,t){let n=await this.request("POST",`/v1/account/connections/${e}/start`,t);if(!n)throw new m("POST",`/v1/account/connections/${e}/start`,404,"This deployment has no /v1/account/connections yet");return n}async connect(e,t){let n=await this.request("PUT",`/v1/account/connections/${e}`,t);if(!n)throw new m("PUT",`/v1/account/connections/${e}`,404,"This deployment has no /v1/account/connections yet");return n}async disconnect(e){return(await this.request("DELETE",`/v1/account/connections/${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 m("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 m("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 m("POST",n,404,"Workspace not found");return s.file}async renameWorkspaceFile(e,t,n){let s=`/v1/workspaces/${encodeURIComponent(e)}/files`,r=await this.request("PATCH",s,{path:t,newPath:n});if(!r)throw new m("PATCH",s,404,"Workspace not found");return r.renamed}async deleteWorkspaceFile(e,t){return(await this.request("DELETE",`/v1/workspaces/${encodeURIComponent(e)}/files`,{path:t}))?.deleted??0}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 m("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 listMcp(e){let t=`/v1/mcp${i(e)}`,n=await this.request("GET",t);if(!n)throw new m("GET",t,404,"Not found");return n.servers??[]}async createMcp(e,t){let n=`/v1/mcp${i(e)}`,s=await this.request("POST",n,t);if(!s)throw new m("POST",n,404,"Not found");return s}async uploadMcpBundle(e){let n=await this.request("POST","/v1/mcp/uploads");if(!n?.uploadUrl)throw new m("POST","/v1/mcp/uploads",502,"response omitted uploadUrl");let s=await this.fetchImpl(n.uploadUrl,{method:"POST",headers:{"Content-Type":"application/javascript"},body:e});if(!s.ok)throw new m("POST","/v1/mcp/uploads",s.status,await s.text());let{storageId:r}=await s.json();if(!r)throw new m("POST","/v1/mcp/uploads",502,"upload response omitted storageId");let a=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(e)),c=Array.from(new Uint8Array(a)).map((l)=>l.toString(16).padStart(2,"0")).join("");return{bundleStorageId:r,sha256:c}}async getMcp(e){return await this.request("GET",`/v1/mcp/${encodeURIComponent(e)}`)}async updateMcp(e,t){return await this.request("PATCH",`/v1/mcp/${encodeURIComponent(e)}`,t)}async deleteMcp(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 m("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 m("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 m("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 m("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 m("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}`,r=await this.request("POST",s,n);if(!r)throw new m("POST",s,404,"Sandbox not found");return r}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 m(e,t,s.status,await s.text());return await s.json()}}function o(e){return typeof process<"u"?process?.env?.[e]:void 0}function i(e){return`?${new URLSearchParams({project:e.project,stage:e.stage}).toString()}`}
1
+ var d=/^[A-Z][A-Z0-9_]*$/;class m 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 Ae(e){if(!d.test(e)||e.length>64)throw Error("envPlaceholder name must match /^[A-Z][A-Z0-9_]*$/ and be at most 64 characters.");return`\${${e}}`}function Z(){return o("BROODS_SESSION_TOKEN")??o("BROODS_ACCOUNT_SECRET")}class R{baseUrl;bearerToken;fetchImpl;constructor(e={}){let t=e.baseUrl??o("BROODS_BASE_URL")??"https://gateway.broods.app",n=e.sessionToken??e.accountSecret??Z();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 m("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 m("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 m("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}/v1/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 m("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 listConnections(){return(await this.request("GET","/v1/account/connections"))?.connections??[]}async getConnection(e){return await this.request("GET",`/v1/account/connections/${e}`)}async startConnection(e,t){let n=await this.request("POST",`/v1/account/connections/${e}/start`,t);if(!n)throw new m("POST",`/v1/account/connections/${e}/start`,404,"This deployment has no /v1/account/connections yet");return n}async connect(e,t){let n=await this.request("PUT",`/v1/account/connections/${e}`,t);if(!n)throw new m("PUT",`/v1/account/connections/${e}`,404,"This deployment has no /v1/account/connections yet");return n}async disconnect(e){return(await this.request("DELETE",`/v1/account/connections/${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 m("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 m("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 m("POST",n,404,"Workspace not found");return s.file}async renameWorkspaceFile(e,t,n){let s=`/v1/workspaces/${encodeURIComponent(e)}/files`,r=await this.request("PATCH",s,{path:t,newPath:n});if(!r)throw new m("PATCH",s,404,"Workspace not found");return r.renamed}async deleteWorkspaceFile(e,t){return(await this.request("DELETE",`/v1/workspaces/${encodeURIComponent(e)}/files`,{path:t}))?.deleted??0}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 m("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 listMcp(e){let t=`/v1/mcp${a(e)}`,n=await this.request("GET",t);if(!n)throw new m("GET",t,404,"Not found");return n.servers??[]}async createMcp(e,t){let n=`/v1/mcp${a(e)}`,s=await this.request("POST",n,t);if(!s)throw new m("POST",n,404,"Not found");return s}async uploadMcpBundle(e){let n=await this.request("POST","/v1/mcp/uploads");if(!n?.uploadUrl)throw new m("POST","/v1/mcp/uploads",502,"response omitted uploadUrl");let s=await this.fetchImpl(n.uploadUrl,{method:"POST",headers:{"Content-Type":"application/javascript"},body:e});if(!s.ok)throw new m("POST","/v1/mcp/uploads",s.status,await s.text());let{storageId:r}=await s.json();if(!r)throw new m("POST","/v1/mcp/uploads",502,"upload response omitted storageId");let c=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(e)),u=Array.from(new Uint8Array(c)).map((l)=>l.toString(16).padStart(2,"0")).join("");return{bundleStorageId:r,sha256:u}}async getMcp(e){return await this.request("GET",`/v1/mcp/${encodeURIComponent(e)}`)}async updateMcp(e,t){return await this.request("PATCH",`/v1/mcp/${encodeURIComponent(e)}`,t)}async deleteMcp(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 m("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 m("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}audit={list:async(e={})=>{let t=await this.request("GET",`/v1/audit${i(e)}`);if(!t)throw new m("GET","/v1/audit",404,"Not found");return t},verify:async(e={})=>{let t=await this.request("GET",`/v1/audit/verify${i(e)}`);if(!t)throw new m("GET","/v1/audit/verify",404,"Not found");return t},getSink:async()=>await this.request("GET","/v1/audit/sink"),setSink:async(e)=>{let t=await this.request("PUT","/v1/audit/sink",e);if(!t)throw new m("PUT","/v1/audit/sink",404,"Not found");return t},deleteSink:async()=>(await this.request("DELETE","/v1/audit/sink"))?.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 m("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 m("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 m("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}`,r=await this.request("POST",s,n);if(!r)throw new m("POST",s,404,"Sandbox not found");return r}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 m(e,t,s.status,await s.text());return await s.json()}}function o(e){return typeof process<"u"?process?.env?.[e]:void 0}function i(e){let t=new URLSearchParams;for(let[n,s]of Object.entries(e))if(s!==void 0)t.set(n,String(s));return t.size>0?`?${t.toString()}`:""}function a(e){return`?${new URLSearchParams({project:e.project,stage:e.stage}).toString()}`}
2
2
  export{m,Ae,Z,R};
@@ -1,5 +1,5 @@
1
1
  function V(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*ge(e){let n=e.getReader(),t=new TextDecoder,o="";try{while(!0){let{done:r,value:i}=await n.read();if(r)break;o+=t.decode(i,{stream:!0});let s=o.split(`
2
- `);o=s.pop()??"";for(let a of s)if(a.startsWith("data: "))yield a.slice(6)}}finally{n.releaseLock()}}var Me=500,Ce=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/,he=/^(?!0{32}$)[0-9a-f]{32}$/;function Pe(e){return e==="DEBUG"||e==="INFO"||e==="WARN"||e==="ERROR"}function Ne(e){if(typeof e!=="object"||e===null)return!1;let n=e;if(n.type==="subscribe")return Se(n);if(n.type==="unsubscribe")return k(n.stream);if(n.type==="fetchTrace")return Ie(n.traceId);return!1}function je(e){return e==="task"||e==="cron"||e==="subtask"}function G(e){return typeof e==="string"&&Ce.test(e)}function Ie(e){return typeof e==="string"&&he.test(e)}function k(e){return e==="logs"||e==="traces"}function Se(e){let n=e.stream;if(!k(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&&!Pe(o))return!1;let r=e.sandboxId;return r===void 0||n==="logs"&&G(r)}import{readFileSync as Re}from"node:fs";import{chmod as B,mkdir as be,writeFile as Ae}from"node:fs/promises";import{homedir as ke}from"node:os";import{dirname as D,join as ve}from"node:path";var S="broods",_="_generated",O=ve(ke(),".broods","config.json"),N="broods.app",P="dashboard",we="gateway";function T(e){let n=process.env.BROODS_TOKEN,t=e??process.env.BROODS_BASE_URL;if(n&&t)return{baseUrl:f(t),token:n,createdAt:new Date().toISOString()};let o=H();if(!o)return null;if(t)return o.logins[f(t)]??null;let r=process.env.BROODS_DASHBOARD_URL;if(r){let i=f(r);return Object.values(o.logins).find((s)=>s.dashboardUrl===i)??null}return o.logins[o.current]??null}async function W(e){let n=H()?.logins??{},t={current:e.baseUrl,logins:{...n,[e.baseUrl]:e}};await be(D(O),{recursive:!0,mode:448}),await B(D(O),448),await K(O,`${JSON.stringify(t,null,2)}
2
+ `);o=s.pop()??"";for(let a of s)if(a.startsWith("data: "))yield a.slice(6)}}finally{n.releaseLock()}}var Me=500,Ce=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/,he=/^(?!0{32}$)[0-9a-f]{32}$/;function Pe(e){return e==="DEBUG"||e==="INFO"||e==="WARN"||e==="ERROR"}function Ne(e){if(typeof e!=="object"||e===null)return!1;let n=e;if(n.type==="subscribe")return Se(n);if(n.type==="unsubscribe")return k(n.stream);if(n.type==="fetchTrace")return Ie(n.traceId);return!1}function je(e){return e==="task"||e==="cron"||e==="subtask"}function G(e){return typeof e==="string"&&Ce.test(e)}function Ie(e){return typeof e==="string"&&he.test(e)}function k(e){return e==="logs"||e==="traces"}function Se(e){let n=e.stream;if(!k(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&&!Pe(o))return!1;let r=e.sandboxId;return r===void 0||n==="logs"&&G(r)}import{readFileSync as Re}from"node:fs";import{chmod as B,mkdir as be,writeFile as Ae}from"node:fs/promises";import{homedir as ke}from"node:os";import{dirname as D,join as ve}from"node:path";var y="broods",_="_generated",O=ve(ke(),".broods","config.json"),N="broods.app",P="dashboard",we="gateway";function T(e){let n=process.env.BROODS_TOKEN,t=e??process.env.BROODS_BASE_URL;if(n&&t)return{baseUrl:f(t),token:n,createdAt:new Date().toISOString()};let o=H();if(!o)return null;if(t)return o.logins[f(t)]??null;let r=process.env.BROODS_DASHBOARD_URL;if(r){let i=f(r);return Object.values(o.logins).find((s)=>s.dashboardUrl===i)??null}return o.logins[o.current]??null}async function W(e){let n=H()?.logins??{},t={current:e.baseUrl,logins:{...n,[e.baseUrl]:e}};await be(D(O),{recursive:!0,mode:448}),await B(D(O),448),await K(O,`${JSON.stringify(t,null,2)}
3
3
  `)}async function K(e,n){await Ae(e,n,{encoding:"utf8",mode:384}),await B(e,384)}function v(){return process.env.BROODS_STAGE}function f(e){let n=e.length;while(n>0&&e.charCodeAt(n-1)===47)n-=1;return e.slice(0,n)}function L(e){try{let n=new URL(e);if(!n.hostname.startsWith(`${P}.`))return;let t=n.hostname.slice(P.length+1);if(t!==N&&!t.endsWith(`.${N}`))return;return n.hostname=`${we}.${t}`,f(n.origin)}catch{return}}function H(){try{let e=JSON.parse(Re(O,"utf8"));if(typeof e.current!=="string"||!e.logins)return null;return{current:e.current,logins:e.logins}}catch{return null}}import{existsSync as q,readFileSync as Z,statSync as Ee}from"node:fs";import{join as X,resolve as Q}from"node:path";var A=new Map,Te=new Set(Object.keys(process.env)),j=new Set;function h(e=process.cwd()){Oe(e);let n=T();return{dashboardUrl:process.env.BROODS_DASHBOARD_URL??n?.dashboardUrl,baseUrl:process.env.BROODS_BASE_URL??n?.baseUrl,token:n?.token,project:process.env.BROODS_PROJECT,stage:v()}}function U(){let e={};for(let[n,t]of Object.entries(process.env))if(typeof t==="string"&&!n.startsWith("BROODS_"))e[n]=t;return e}function M(e,n=process.cwd()){let t=process.env[e];if(t===void 0)return!1;let o=Q(n);for(let r of[".env.local",".env"]){let i=X(o,r);if(!q(i))continue;let s=ee(Z(i,"utf8"));if(s[e]!==void 0)return s[e]!==t}return!0}function Oe(e){let n=Q(e),t=process.env.BROODS_RELOAD_ENV==="1";for(let o of[".env",".env.local"]){let r=X(n,o);if(!q(r)){A.delete(r);continue}let i=Ee(r).mtimeMs,s=A.get(r);if(s&&s.mtimeMs===i)continue;A.set(r,{mtimeMs:i});for(let[a,p]of Object.entries(ee(Z(r,"utf8"))))if(t||!Te.has(a)||j.has(a))process.env[a]=p,j.add(a)}}function ee(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,i=r.indexOf("=");if(i<=0)continue;let s=r.slice(0,i).trim();if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(s))continue;n[s]=_e(r.slice(i+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}var E="https://gateway.broods.app";class ye extends Error{accepted;constructor(e){super(`Ingress ${e.eventId} was accepted asynchronously with status ${e.status??"queued"}`);this.accepted=e;this.name="IngressAcceptedError"}}class F{baseUrl;apiKey;fetchImpl;constructor(e={}){h(),this.baseUrl=J(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||E),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:(i)=>this.run({...i,agentName:o,agentId:r}),runAsync:(i)=>this.runAsync({...i,agentName:o,agentId:r}),continue:(i)=>this.continue({...i,agentId:r}),stream:(i)=>this.stream({...i,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 i of r)if(t.push(i),i.type==="text-delta")o+=i.text;return{text:o,events:t}}async*stream(e,n){let t=n?{...n,agentId:e.id,agentName:e.name}:e,o=ne(t,"cli"),r=n?this.scopedUrl(e):`${this.baseUrl}/v1/runs`,i=await this.openStream(o,r);if(!i.ok)throw Error(`Run failed: ${i.status} ${await i.text()}`);if(i.status===202)throw new ye(te(await i.json(),o));if(!i.body)throw Error("Run response has no body");for await(let s of ge(i.body)){let a;try{a=JSON.parse(s)}catch{continue}if(a.type==="error")throw Error(`Agent run failed: ${$e(a.error)}`);yield a}}async runAsync(e,n){let t=n?{...n,agentId:e.id,agentName:e.name}:e,o={...ne(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},i=t?this.scopedUrl(t):`${this.baseUrl}/v1/runs`;return this.postAcceptedRun(i,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 en(o,n.signal)}throw Error("Polling timeout")}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 ze(o,"202 JSON")}`);return this.asyncAgentRun(te(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 ${E} 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 nn(this.baseUrl,e)}}function J(e){let n=e.trim(),t=/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(n)?n:`https://${n}`;return f(t)}function ne(e,n){let t=e.eventId??`${n}-${crypto.randomUUID()}`;return{agentId:e.agentId,eventId:t,conversationKey:e.conversationKey??t,events:V(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 $e(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 te(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=e.runId;if(typeof o!=="string"||o.length===0)throw Error("Async response missing runId");let r=typeof e.eventId==="string"?e.eventId:o,{status:i,requestedMode:s}=e;return{statusUrl:t,runId:o,eventId:r,agentId:n.agentId,...i==="accepted"||i==="queued"||i==="applied"||i==="processing"?{status:i}:{},...s==="reject"||s==="followup"||s==="collect"||s==="steer"?{requestedMode:s}:{}}}async function ze(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 en(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 nn(e,n){return`${J(e)}/v1/runs/${encodeURIComponent(n)}`}var tn=2000,oe=0,C=1;class xe{baseUrl;apiKey;WebSocketImpl;connectTimeoutMs;constructor(e={}){if(h(),this.baseUrl=ue(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||E),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??tn}subscribe(e,n={}){let t=this.resolveWebSocket(),o=this.buildUrl(e),r=on(e),i=ce(this.baseUrl),s=new t(o,z(this.apiKey)),a=!1,p=!1,d=setTimeout(()=>{if(!a)y(i)},this.connectTimeoutMs),x=(l=1000,b="client closed")=>{if(p)return;if(p=!0,clearTimeout(d),e.signal?.removeEventListener("abort",R),s.readyState===C||s.readyState===oe)s.close(l,b)},y=(l)=>{n.onError?.(l),x(1011,"websocket error")},me=()=>{n.onDone?.(),x(1000,"done")},R=()=>{if(s.readyState===C)s.send(JSON.stringify({type:"cancel"}));y(Error("WebSocket run aborted."))};return e.signal?.addEventListener("abort",R,{once:!0}),s.onopen=()=>{if(a=!0,clearTimeout(d),e.signal?.aborted){R();return}s.send(JSON.stringify({type:"execute",agentId:r,...e.answers?{answers:e.answers}:{events:V(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}:{}}))},s.onmessage=(l)=>{if(typeof l.data!=="string")return;let b=se(l.data);if(!b)return;let I=ae(b,n);switch(n.onMessage?.(I),I.type){case"meta":n.onMeta?.(I);break;case"done":me();break;case"error":y(Error(re(I.error)));break}},s.onerror=()=>{y(i)},s.onclose=(l)=>{if(clearTimeout(d),e.signal?.removeEventListener("abort",R),p||l.code===1000)return;y(Error(l.reason||"WebSocket connection closed."))},{url:o,sendControl:function(l){if(s.readyState!==C)throw Error("WebSocket is not open");s.send(JSON.stringify({type:"control",...l}))},close:x}}attach(e,n={}){let t=this.resolveWebSocket(),o=this.buildUrl(e),r=new t(o,z(this.apiKey)),i=!1,s=(a=1000,p="client closed")=>{if(i)return;if(i=!0,r.readyState===C||r.readyState===oe)r.close(a,p)};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 p=se(a.data);if(!p)return;let d=ae(p,n);if(n.onMessage?.(d),d.type==="done")n.onDone?.();if(d.type==="error")n.onError?.(Error(re(d.error)))},r.onerror=()=>n.onError?.(ce(this.baseUrl)),r.onclose=(a)=>{if(!i&&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:s}}async*stream(e){let n=[],t=!1,o=null,r=null,i=()=>{r?.(),r=null},s=this.subscribe(e,{onMessage:function(a){n.push(a),i()},onDone:function(){t=!0,i()},onError:function(a){o=a,i()}});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{s.close()}}buildUrl(e){let n=rn(e),t=e.projectSlug??e.agent?.projectSlug,o=e.stageSlug??e.agent?.stageSlug,r=t&&o?`/projects/${encodeURIComponent(t)}/stages/${encodeURIComponent(o)}`:"";return`${Y(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 Y(e){let n=new URL(ue(e));if(n.protocol==="https:")n.protocol="wss:";if(n.protocol==="http:")n.protocol="ws:";return f(n.toString())}function z(e){return["broods.v1",`broods.token.${e}`]}function re(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 ue(e){let n=e.trim();if(/^wss?:\/\//.test(n))return f(n);return J(n)}function se(e){try{let n=JSON.parse(e);return typeof n==="object"&&n!==null&&typeof n.type==="string"?n:null}catch{return null}}function on(e){let n=e.agentId??e.agent?.id??e.endpointId;if(!n)throw Error("WebSocket run requires agent, agentId, or endpointId.");return n}function rn(e){let n=e.endpointId??e.agent?.endpointId??e.agent?.id;if(!n)throw Error("WebSocket run requires agent or endpointId.");return n}function ae(e,n){if(e.type!=="output")return e;let t=e;return n.onOutput?.(t),t.data}function ce(e){return Error(`Cannot access the WebSocket service at ${e}.`)}var pe=Symbol.for("broods.resource"),de=Symbol.for("broods.config"),fe=Symbol.for("broods.connection"),le=/^[A-Z][A-Z0-9_]{0,63}$/,qe=new Proxy((e)=>{if(!le.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"&&le.test(n))throw Error(`env.${n} is not supported; use env("${n}")`);return Reflect.get(e,n,t)}});function De(e){let{name:n,description:t,...o}=e;return m("agent",n,t,o)}function Le(e){return{[de]:!0,config:e}}function Ue(e){let{name:n,description:t,...o}=e;return m("cron",n,t,o)}function Fe(e){return e}function Be(e){let{name:n,description:t,...o}=e;return m("mcp",n,t,o)}function Ze(e){let{name:n,description:t,...o}=e;return m("policy",n,t,o)}function He(e){let{name:n,description:t,...o}=e;return m("sandbox",n,t,o)}function Ve(e){let{name:n,description:t,...o}=e;return m("skill",n,t,o)}function Ge(e){let{name:n,description:t,...o}=e;return m("workspace",n,t,o)}function We(e){return u("discord",e)}function Ke(e){return u("github",e)}function Je(e){return u("gchat",e)}function Ye(e){return u("instagram",e)}function Xe(e){return u("linear",e)}function Qe(e){return u("matrix",e)}function et(e){return u("messenger",e)}function tt(e){return u("pancake",e)}function nt(e){return u("slack",e)}function rt(e){return u("teams",e)}function ot(e){return u("telegram",e)}function st(e){return u("twilio",e)}function it(e){return u("whatsapp",e)}function at(e){return u("zalo",e)}function ct(e){let{name:n,description:t,channelId:o,guildId:r,...i}=e;return c(n,t,o,r,i)}function lt(e){let{name:n,description:t,repo:o,...r}=e,[i,...s]=o.split("/");if(!i||s.length!==1||!s[0])throw Error(`Channel "${n}" repo must be "owner/name", not "${o}"`);return c(n,t,o,i,r)}function ut(e){let{name:n,description:t,spaceName:o,...r}=e;return c(n,t,o,void 0,r)}function dt(e){let{name:n,description:t,igsid:o,...r}=e;return c(n,t,o,void 0,r)}function pt(e){let{name:n,description:t,team:o,...r}=e;return c(n,t,o,void 0,r)}function mt(e){let{name:n,description:t,channelId:o,...r}=e;return c(n,t,o,void 0,r)}function ht(e){let{name:n,description:t,psid:o,...r}=e;return c(n,t,o,void 0,r)}function ft(e){let{name:n,description:t,conversationId:o,...r}=e;return c(n,t,o,void 0,r)}function gt(e){let{name:n,description:t,channelId:o,teamId:r,...i}=e;return c(n,t,o,r,i)}function yt(e){let{name:n,description:t,channelId:o,teamId:r,...i}=e;return c(n,t,o,r,i)}function St(e){let{name:n,description:t,chatId:o,...r}=e;return c(n,t,o,void 0,r)}function bt(e){let{name:n,description:t,from:o,...r}=e;return c(n,t,o,void 0,r)}function vt(e){let{name:n,description:t,waId:o,...r}=e;return c(n,t,o,void 0,r)}function _t(e){let{name:n,description:t,chatId:o,...r}=e;return c(n,t,o,void 0,r)}function ie(e){return Boolean(e&&typeof e==="object"&&e[de])}function w(e){return Boolean(e&&typeof e==="object"&&e[fe])}function g(e){return Boolean(e&&typeof e==="object"&&e[pe])}function c(e,n,t,o,r){return m("channelRecord",e,n,{...r,externalId:t,...o?{workspaceRef:o}:{}})}function u(e,n){let{partition:t,...o}=n;return{[fe]:!0,kind:"connection",type:e,...t?{partition:t}:{},config:o}}function m(e,n,t,o){return{[pe]:!0,kind:e,name:n,...t?{description:t}:{},config:o}}
5
- export{V,ge,Me,Pe,Ne,je,G,Ie,S,_,O,T,W,K,v,f,L,h,U,M,E,ye,F,J,xe,Y,z,qe,De,Le,Ue,Fe,Be,Ze,He,Ve,Ge,We,Ke,Je,Ye,Xe,Qe,et,tt,nt,rt,ot,st,it,at,ct,lt,ut,dt,pt,mt,ht,ft,gt,yt,St,bt,vt,_t,ie,w,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}var E="https://gateway.broods.app";class ye extends Error{accepted;constructor(e){super(`Ingress ${e.eventId} was accepted asynchronously with status ${e.status??"queued"}`);this.accepted=e;this.name="IngressAcceptedError"}}class F{baseUrl;apiKey;fetchImpl;constructor(e={}){h(),this.baseUrl=J(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||E),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:(i)=>this.run({...i,agentName:o,agentId:r}),runAsync:(i)=>this.runAsync({...i,agentName:o,agentId:r}),continue:(i)=>this.continue({...i,agentId:r}),stream:(i)=>this.stream({...i,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 i of r)if(t.push(i),i.type==="text-delta")o+=i.text;return{text:o,events:t}}async*stream(e,n){let t=n?{...n,agentId:e.id,agentName:e.name}:e,o=ne(t,"cli"),r=n?this.scopedUrl(e):`${this.baseUrl}/v1/runs`,i=await this.openStream(o,r);if(!i.ok)throw Error(`Run failed: ${i.status} ${await i.text()}`);if(i.status===202)throw new ye(te(await i.json(),o));if(!i.body)throw Error("Run response has no body");for await(let s of ge(i.body)){let a;try{a=JSON.parse(s)}catch{continue}if(a.type==="error")throw Error(`Agent run failed: ${$e(a.error)}`);yield a}}async runAsync(e,n){let t=n?{...n,agentId:e.id,agentName:e.name}:e,o={...ne(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},i=t?this.scopedUrl(t):`${this.baseUrl}/v1/runs`;return this.postAcceptedRun(i,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 en(o,n.signal)}throw Error("Polling timeout")}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 ze(o,"202 JSON")}`);return this.asyncAgentRun(te(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 ${E} 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 nn(this.baseUrl,e)}}function J(e){let n=e.trim(),t=/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(n)?n:`https://${n}`;return f(t)}function ne(e,n){let t=e.eventId??`${n}-${crypto.randomUUID()}`;return{agentId:e.agentId,eventId:t,conversationKey:e.conversationKey??t,events:V(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 $e(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 te(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=e.runId;if(typeof o!=="string"||o.length===0)throw Error("Async response missing runId");let r=typeof e.eventId==="string"?e.eventId:o,{status:i,requestedMode:s}=e;return{statusUrl:t,runId:o,eventId:r,agentId:n.agentId,...i==="accepted"||i==="queued"||i==="applied"||i==="processing"?{status:i}:{},...s==="reject"||s==="followup"||s==="collect"||s==="steer"?{requestedMode:s}:{}}}async function ze(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 en(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 nn(e,n){return`${J(e)}/v1/runs/${encodeURIComponent(n)}`}var tn=2000,oe=0,S=1;class xe{baseUrl;apiKey;WebSocketImpl;connectTimeoutMs;constructor(e={}){if(h(),this.baseUrl=ue(e.baseUrl||e.host||process.env.BROODS_BASE_URL||process.env.BROODS_HOST||E),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??tn}subscribe(e,n={}){let t=this.resolveWebSocket(),o=this.buildUrl(e),r=on(e),i=ce(this.baseUrl),s=new t(o,z(this.apiKey)),a=!1,p=!1,d=setTimeout(()=>{if(!a)C(i)},this.connectTimeoutMs),x=(l=1000,b="client closed")=>{if(p)return;if(p=!0,clearTimeout(d),e.signal?.removeEventListener("abort",R),s.readyState===S||s.readyState===oe)s.close(l,b)},C=(l)=>{n.onError?.(l),x(1011,"websocket error")},me=()=>{n.onDone?.(),x(1000,"done")},R=()=>{if(s.readyState===S)s.send(JSON.stringify({type:"cancel"}));C(Error("WebSocket run aborted."))};return e.signal?.addEventListener("abort",R,{once:!0}),s.onopen=()=>{if(a=!0,clearTimeout(d),e.signal?.aborted){R();return}s.send(JSON.stringify({type:"execute",agentId:r,...e.answers?{answers:e.answers}:{events:V(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}:{}}))},s.onmessage=(l)=>{if(typeof l.data!=="string")return;let b=se(l.data);if(!b)return;let I=ae(b,n);switch(n.onMessage?.(I),I.type){case"meta":n.onMeta?.(I);break;case"done":me();break;case"error":C(Error(re(I.error)));break}},s.onerror=()=>{C(i)},s.onclose=(l)=>{if(clearTimeout(d),e.signal?.removeEventListener("abort",R),p||l.code===1000)return;C(Error(l.reason||"WebSocket connection closed."))},{url:o,sendControl:function(l){if(s.readyState!==S)throw Error("WebSocket is not open");s.send(JSON.stringify({type:"control",...l}))},close:x}}attach(e,n={}){let t=this.resolveWebSocket(),o=this.buildUrl(e),r=new t(o,z(this.apiKey)),i=!1,s=(a=1000,p="client closed")=>{if(i)return;if(i=!0,r.readyState===S||r.readyState===oe)r.close(a,p)};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 p=se(a.data);if(!p)return;let d=ae(p,n);if(n.onMessage?.(d),d.type==="done")n.onDone?.();if(d.type==="error")n.onError?.(Error(re(d.error)))},r.onerror=()=>n.onError?.(ce(this.baseUrl)),r.onclose=(a)=>{if(!i&&a.code!==1000)n.onError?.(Error(a.reason||"WebSocket connection closed."))},{url:o,sendControl:function(a){if(r.readyState!==S)throw Error("WebSocket is not open");r.send(JSON.stringify({type:"control",...a}))},close:s}}async*stream(e){let n=[],t=!1,o=null,r=null,i=()=>{r?.(),r=null},s=this.subscribe(e,{onMessage:function(a){n.push(a),i()},onDone:function(){t=!0,i()},onError:function(a){o=a,i()}});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{s.close()}}buildUrl(e){let n=rn(e),t=e.projectSlug??e.agent?.projectSlug,o=e.stageSlug??e.agent?.stageSlug,r=t&&o?`/projects/${encodeURIComponent(t)}/stages/${encodeURIComponent(o)}`:"";return`${Y(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 Y(e){let n=new URL(ue(e));if(n.protocol==="https:")n.protocol="wss:";if(n.protocol==="http:")n.protocol="ws:";return f(n.toString())}function z(e){return["broods.v1",`broods.token.${e}`]}function re(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 ue(e){let n=e.trim();if(/^wss?:\/\//.test(n))return f(n);return J(n)}function se(e){try{let n=JSON.parse(e);return typeof n==="object"&&n!==null&&typeof n.type==="string"?n:null}catch{return null}}function on(e){let n=e.agentId??e.agent?.id??e.endpointId;if(!n)throw Error("WebSocket run requires agent, agentId, or endpointId.");return n}function rn(e){let n=e.endpointId??e.agent?.endpointId??e.agent?.id;if(!n)throw Error("WebSocket run requires agent or endpointId.");return n}function ae(e,n){if(e.type!=="output")return e;let t=e;return n.onOutput?.(t),t.data}function ce(e){return Error(`Cannot access the WebSocket service at ${e}.`)}var pe=Symbol.for("broods.resource"),de=Symbol.for("broods.config"),fe=Symbol.for("broods.connection"),le=/^[A-Z][A-Z0-9_]{0,63}$/,qe=new Proxy((e)=>{if(!le.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"&&le.test(n))throw Error(`env.${n} is not supported; use env("${n}")`);return Reflect.get(e,n,t)}});function De(e){let{name:n,description:t,...o}=e;return m("agent",n,t,o)}function Le(e){return{[de]:!0,config:e}}function Ue(e){let{name:n,description:t,...o}=e;return m("cron",n,t,o)}function Fe(e){return e}function Be(e){let{name:n,description:t,...o}=e;return m("mcp",n,t,o)}function Ze(e){let{name:n,description:t,...o}=e;return m("policy",n,t,o)}function He(e){let{name:n,description:t,...o}=e;return m("sandbox",n,t,o)}function Ve(e){let{name:n,description:t,...o}=e;return m("skill",n,t,o)}function Ge(e){let{name:n,description:t,...o}=e;return m("workspace",n,t,o)}function We(e){return u("discord",e)}function Ke(e){return u("github",e)}function Je(e){return u("gchat",e)}function Ye(e){return u("instagram",e)}function Xe(e){return u("linear",e)}function Qe(e){return u("matrix",e)}function et(e){return u("messenger",e)}function tt(e){return u("pancake",e)}function nt(e){return u("slack",e)}function rt(e){return u("teams",e)}function ot(e){return u("telegram",e)}function st(e){return u("twilio",e)}function it(e){return u("whatsapp",e)}function at(e){return u("zalo",e)}function ct(e){let{name:n,description:t,channelId:o,guildId:r,...i}=e;return c(n,t,o,r,i)}function lt(e){let{name:n,description:t,repo:o,...r}=e,[i,...s]=o.split("/");if(!i||s.length!==1||!s[0])throw Error(`Channel "${n}" repo must be "owner/name", not "${o}"`);return c(n,t,o,i,r)}function ut(e){let{name:n,description:t,spaceName:o,...r}=e;return c(n,t,o,void 0,r)}function dt(e){let{name:n,description:t,igsid:o,...r}=e;return c(n,t,o,void 0,r)}function pt(e){let{name:n,description:t,team:o,...r}=e;return c(n,t,o,void 0,r)}function mt(e){let{name:n,description:t,channelId:o,...r}=e;return c(n,t,o,void 0,r)}function ht(e){let{name:n,description:t,psid:o,...r}=e;return c(n,t,o,void 0,r)}function ft(e){let{name:n,description:t,conversationId:o,...r}=e;return c(n,t,o,void 0,r)}function gt(e){let{name:n,description:t,channelId:o,teamId:r,...i}=e;return c(n,t,o,r,i)}function yt(e){let{name:n,description:t,channelId:o,teamId:r,...i}=e;return c(n,t,o,r,i)}function St(e){let{name:n,description:t,chatId:o,...r}=e;return c(n,t,o,void 0,r)}function bt(e){let{name:n,description:t,from:o,...r}=e;return c(n,t,o,void 0,r)}function vt(e){let{name:n,description:t,waId:o,...r}=e;return c(n,t,o,void 0,r)}function _t(e){let{name:n,description:t,chatId:o,...r}=e;return c(n,t,o,void 0,r)}function ie(e){return Boolean(e&&typeof e==="object"&&e[de])}function w(e){return Boolean(e&&typeof e==="object"&&e[fe])}function g(e){return Boolean(e&&typeof e==="object"&&e[pe])}function c(e,n,t,o,r){return m("channelRecord",e,n,{...r,externalId:t,...o?{workspaceRef:o}:{}})}function u(e,n){let{partition:t,...o}=n;return{[fe]:!0,kind:"connection",type:e,...t?{partition:t}:{},config:o}}function m(e,n,t,o){return{[pe]:!0,kind:e,name:n,...t?{description:t}:{},config:o}}
5
+ export{V,ge,Me,Pe,Ne,je,G,Ie,y,_,O,T,W,K,v,f,L,h,U,M,E,ye,F,J,xe,Y,z,qe,De,Le,Ue,Fe,Be,Ze,He,Ve,Ge,We,Ke,Je,Ye,Xe,Qe,et,tt,nt,rt,ot,st,it,at,ct,lt,ut,dt,pt,mt,ht,ft,gt,yt,St,bt,vt,_t,ie,w,g};
@@ -1 +1 @@
1
- import{y,de,pe,ke}from"./chunk-naxtmb6t.js";var c=()=>{let a={string:{unit:"characters",verb:"to have"},file:{unit:"bytes",verb:"to have"},array:{unit:"items",verb:"to have"},set:{unit:"items",verb:"to have"},map:{unit:"entries",verb:"to have"}};function n(t){return a[t]??null}let d={regex:"input",email:"email address",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO datetime",date:"ISO date",time:"ISO time",duration:"ISO duration",ipv4:"IPv4 address",ipv6:"IPv6 address",mac:"MAC address",cidrv4:"IPv4 range",cidrv6:"IPv6 range",base64:"base64-encoded string",base64url:"base64url-encoded string",json_string:"JSON string",e164:"E.164 number",jwt:"JWT",template_literal:"input"},o={nan:"NaN"};return(t)=>{switch(t.code){case"invalid_type":{let e=o[t.expected]??t.expected,r=ke(t.input),l=o[r]??r;return`Invalid input: expected ${e}, received ${l}`}case"invalid_value":if(t.values.length===1)return`Invalid input: expected ${pe(t.values[0])}`;return`Invalid option: expected one of ${de(t.values,"|")}`;case"too_big":{let e=t.inclusive?"<=":"<",r=n(t.origin);if(r)return`Too big: expected ${t.origin??"value"} to have ${e}${t.maximum.toString()} ${r.unit??"elements"}`;return`Too big: expected ${t.origin??"value"} to be ${e}${t.maximum.toString()}`}case"too_small":{let e=t.inclusive?">=":">",r=n(t.origin);if(r)return`Too small: expected ${t.origin} to have ${e}${t.minimum.toString()} ${r.unit}`;return`Too small: expected ${t.origin} to be ${e}${t.minimum.toString()}`}case"invalid_format":{let e=t;if(e.format==="starts_with")return`Invalid string: must start with "${e.prefix}"`;if(e.format==="ends_with")return`Invalid string: must end with "${e.suffix}"`;if(e.format==="includes")return`Invalid string: must include "${e.includes}"`;if(e.format==="regex")return`Invalid string: must match pattern ${e.pattern}`;return`Invalid ${d[e.format]??t.format}`}case"not_multiple_of":return`Invalid number: must be a multiple of ${t.divisor}`;case"unrecognized_keys":return`Unrecognized key${t.keys.length>1?"s":""}: ${de(t.keys,", ")}`;case"invalid_key":return`Invalid key in ${t.origin}`;case"invalid_union":if(t.options&&Array.isArray(t.options)&&t.options.length>0)return`Invalid discriminator value. Expected ${t.options.map((r)=>`'${r}'`).join(" | ")}`;return"Invalid input";case"invalid_element":return`Invalid value in ${t.origin}`;default:return"Invalid input"}}};function i(){return{localeError:c()}}y(i());
1
+ import{S,de,pe,ke}from"./chunk-n890pdvc.js";var c=()=>{let a={string:{unit:"characters",verb:"to have"},file:{unit:"bytes",verb:"to have"},array:{unit:"items",verb:"to have"},set:{unit:"items",verb:"to have"},map:{unit:"entries",verb:"to have"}};function n(t){return a[t]??null}let d={regex:"input",email:"email address",url:"URL",emoji:"emoji",uuid:"UUID",uuidv4:"UUIDv4",uuidv6:"UUIDv6",nanoid:"nanoid",guid:"GUID",cuid:"cuid",cuid2:"cuid2",ulid:"ULID",xid:"XID",ksuid:"KSUID",datetime:"ISO datetime",date:"ISO date",time:"ISO time",duration:"ISO duration",ipv4:"IPv4 address",ipv6:"IPv6 address",mac:"MAC address",cidrv4:"IPv4 range",cidrv6:"IPv6 range",base64:"base64-encoded string",base64url:"base64url-encoded string",json_string:"JSON string",e164:"E.164 number",jwt:"JWT",template_literal:"input"},o={nan:"NaN"};return(t)=>{switch(t.code){case"invalid_type":{let e=o[t.expected]??t.expected,r=ke(t.input),l=o[r]??r;return`Invalid input: expected ${e}, received ${l}`}case"invalid_value":if(t.values.length===1)return`Invalid input: expected ${pe(t.values[0])}`;return`Invalid option: expected one of ${de(t.values,"|")}`;case"too_big":{let e=t.inclusive?"<=":"<",r=n(t.origin);if(r)return`Too big: expected ${t.origin??"value"} to have ${e}${t.maximum.toString()} ${r.unit??"elements"}`;return`Too big: expected ${t.origin??"value"} to be ${e}${t.maximum.toString()}`}case"too_small":{let e=t.inclusive?">=":">",r=n(t.origin);if(r)return`Too small: expected ${t.origin} to have ${e}${t.minimum.toString()} ${r.unit}`;return`Too small: expected ${t.origin} to be ${e}${t.minimum.toString()}`}case"invalid_format":{let e=t;if(e.format==="starts_with")return`Invalid string: must start with "${e.prefix}"`;if(e.format==="ends_with")return`Invalid string: must end with "${e.suffix}"`;if(e.format==="includes")return`Invalid string: must include "${e.includes}"`;if(e.format==="regex")return`Invalid string: must match pattern ${e.pattern}`;return`Invalid ${d[e.format]??t.format}`}case"not_multiple_of":return`Invalid number: must be a multiple of ${t.divisor}`;case"unrecognized_keys":return`Unrecognized key${t.keys.length>1?"s":""}: ${de(t.keys,", ")}`;case"invalid_key":return`Invalid key in ${t.origin}`;case"invalid_union":if(t.options&&Array.isArray(t.options)&&t.options.length>0)return`Invalid discriminator value. Expected ${t.options.map((r)=>`'${r}'`).join(" | ")}`;return"Invalid input";case"invalid_element":return`Invalid value in ${t.origin}`;default:return"Invalid input"}}};function i(){return{localeError:c()}}S(i());