broods 0.2.0 → 0.3.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.
@@ -0,0 +1,271 @@
1
+ // @bun
2
+ // src/account.ts
3
+ var DEFAULT_ACCOUNT_BASE_URL = "https://gateway.broods.app";
4
+
5
+ class BroodsAccountApiError extends Error {
6
+ status;
7
+ body;
8
+ constructor(method, path, status, body) {
9
+ super(`${method} ${path} failed with ${status}: ${body}`);
10
+ this.name = "BroodsAccountApiError";
11
+ this.status = status;
12
+ this.body = body;
13
+ }
14
+ }
15
+ function envVar(name) {
16
+ return typeof process !== "undefined" ? process?.env?.[name] : undefined;
17
+ }
18
+
19
+ class BroodsAccountClient {
20
+ baseUrl;
21
+ accountSecret;
22
+ fetchImpl;
23
+ constructor(options = {}) {
24
+ const baseUrl = options.baseUrl ?? envVar("BROODS_BASE_URL") ?? DEFAULT_ACCOUNT_BASE_URL;
25
+ const accountSecret = options.accountSecret ?? envVar("BROODS_ACCOUNT_SECRET");
26
+ if (!accountSecret)
27
+ throw new Error("BroodsAccountClient requires an accountSecret (or BROODS_ACCOUNT_SECRET).");
28
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
29
+ this.accountSecret = accountSecret;
30
+ this.fetchImpl = options.fetch ?? fetch;
31
+ }
32
+ async getAccount() {
33
+ const result = await this.request("GET", "/v1/account");
34
+ if (!result)
35
+ throw new BroodsAccountApiError("GET", "/v1/account", 404, "Account not found");
36
+ return result.account;
37
+ }
38
+ async updateAccount(patch) {
39
+ const result = await this.request("PATCH", "/v1/account", patch);
40
+ return result?.account ?? null;
41
+ }
42
+ async rotateSecret() {
43
+ const result = await this.request("POST", "/v1/account/rotate-secret");
44
+ if (!result)
45
+ throw new BroodsAccountApiError("POST", "/v1/account/rotate-secret", 404, "Account not found");
46
+ return result;
47
+ }
48
+ async deleteAccount() {
49
+ const result = await this.request("DELETE", "/v1/account");
50
+ return result ?? { deleted: false };
51
+ }
52
+ webhookUrl(accountId, agentId, channelType) {
53
+ const segments = [accountId, agentId, channelType].map(encodeURIComponent);
54
+ return `${this.baseUrl}/webhooks/${segments.join("/")}`;
55
+ }
56
+ async listAgents() {
57
+ const result = await this.request("GET", "/v1/agents");
58
+ return result?.agents ?? [];
59
+ }
60
+ async createAgent(input) {
61
+ const result = await this.request("POST", "/v1/agents", input);
62
+ if (!result)
63
+ throw new BroodsAccountApiError("POST", "/v1/agents", 404, "Not found");
64
+ return result;
65
+ }
66
+ async getAgent(agentId) {
67
+ return await this.request("GET", `/v1/agents/${encodeURIComponent(agentId)}`);
68
+ }
69
+ async updateAgent(agentId, patch) {
70
+ return await this.request("PATCH", `/v1/agents/${encodeURIComponent(agentId)}`, patch);
71
+ }
72
+ async deleteAgent(agentId) {
73
+ const result = await this.request("DELETE", `/v1/agents/${encodeURIComponent(agentId)}`);
74
+ return result?.deleted ?? false;
75
+ }
76
+ async listCrons() {
77
+ const result = await this.request("GET", "/v1/crons");
78
+ return result?.crons ?? [];
79
+ }
80
+ async createCron(input) {
81
+ const result = await this.request("POST", "/v1/crons", input);
82
+ if (!result)
83
+ throw new BroodsAccountApiError("POST", "/v1/crons", 404, "Not found");
84
+ return result;
85
+ }
86
+ async getCron(cronId) {
87
+ return await this.request("GET", `/v1/crons/${encodeURIComponent(cronId)}`);
88
+ }
89
+ async updateCron(cronId, patch) {
90
+ return await this.request("PATCH", `/v1/crons/${encodeURIComponent(cronId)}`, patch);
91
+ }
92
+ async deleteCron(cronId) {
93
+ const result = await this.request("DELETE", `/v1/crons/${encodeURIComponent(cronId)}`);
94
+ return result?.deleted ?? false;
95
+ }
96
+ async listCronRuns(cronId, options = {}) {
97
+ const query = options.limit !== undefined ? `?limit=${options.limit}` : "";
98
+ const result = await this.request("GET", `/v1/crons/${encodeURIComponent(cronId)}/runs${query}`);
99
+ return result?.runs ?? [];
100
+ }
101
+ async listWorkspaces() {
102
+ const result = await this.request("GET", "/v1/workspaces");
103
+ return result?.workspaces ?? [];
104
+ }
105
+ async createWorkspace(input) {
106
+ const result = await this.request("POST", "/v1/workspaces", input);
107
+ if (!result)
108
+ throw new BroodsAccountApiError("POST", "/v1/workspaces", 404, "Not found");
109
+ return result;
110
+ }
111
+ async getWorkspace(workspaceId) {
112
+ return await this.request("GET", `/v1/workspaces/${encodeURIComponent(workspaceId)}`);
113
+ }
114
+ async updateWorkspace(workspaceId, patch) {
115
+ return await this.request("PATCH", `/v1/workspaces/${encodeURIComponent(workspaceId)}`, patch);
116
+ }
117
+ async deleteWorkspace(workspaceId) {
118
+ const result = await this.request("DELETE", `/v1/workspaces/${encodeURIComponent(workspaceId)}`);
119
+ return result?.deleted ?? false;
120
+ }
121
+ async listWorkspaceFiles(workspaceId) {
122
+ const result = await this.request("GET", `/v1/workspaces/${encodeURIComponent(workspaceId)}/files`);
123
+ return result?.files ?? [];
124
+ }
125
+ async getWorkspaceFileUrl(workspaceId, path) {
126
+ const query = `?path=${encodeURIComponent(path)}`;
127
+ const result = await this.request("GET", `/v1/workspaces/${encodeURIComponent(workspaceId)}/files${query}`);
128
+ return result?.url ?? null;
129
+ }
130
+ async uploadWorkspaceFile(workspaceId, input) {
131
+ const path = `/v1/workspaces/${encodeURIComponent(workspaceId)}/files`;
132
+ const result = await this.request("POST", path, input);
133
+ if (!result)
134
+ throw new BroodsAccountApiError("POST", path, 404, "Workspace not found");
135
+ return result.file;
136
+ }
137
+ async renameWorkspaceFile(workspaceId, path, newPath) {
138
+ const result = await this.request("PATCH", `/v1/workspaces/${encodeURIComponent(workspaceId)}/files`, { path, newPath });
139
+ return result?.renamed ?? false;
140
+ }
141
+ async deleteWorkspaceFile(workspaceId, path) {
142
+ const result = await this.request("DELETE", `/v1/workspaces/${encodeURIComponent(workspaceId)}/files`, { path });
143
+ return result?.deleted ?? false;
144
+ }
145
+ async listSandboxes() {
146
+ const result = await this.request("GET", "/v1/sandboxes");
147
+ return result?.sandboxes ?? [];
148
+ }
149
+ async createSandbox(input) {
150
+ const result = await this.request("POST", "/v1/sandboxes", input);
151
+ if (!result)
152
+ throw new BroodsAccountApiError("POST", "/v1/sandboxes", 404, "Not found");
153
+ return result;
154
+ }
155
+ async getSandbox(sandboxId) {
156
+ return await this.request("GET", `/v1/sandboxes/${encodeURIComponent(sandboxId)}`);
157
+ }
158
+ async updateSandbox(sandboxId, patch) {
159
+ return await this.request("PATCH", `/v1/sandboxes/${encodeURIComponent(sandboxId)}`, patch);
160
+ }
161
+ async deleteSandbox(sandboxId) {
162
+ const result = await this.request("DELETE", `/v1/sandboxes/${encodeURIComponent(sandboxId)}`);
163
+ return result?.deleted ?? false;
164
+ }
165
+ async suspendSandbox(sandboxId, reservationKey) {
166
+ return await this.sandboxAction(sandboxId, "suspend", { reservationKey });
167
+ }
168
+ async resumeSandbox(sandboxId, reservationKey) {
169
+ return await this.sandboxAction(sandboxId, "resume", { reservationKey });
170
+ }
171
+ async terminateSandbox(sandboxId, reservationKey) {
172
+ return await this.sandboxAction(sandboxId, "terminate", { reservationKey });
173
+ }
174
+ async snapshotSandbox(sandboxId, reservationKey, name) {
175
+ return await this.sandboxAction(sandboxId, "snapshot", { reservationKey, name });
176
+ }
177
+ async openSandboxTerminal(sandboxId, reservationKey) {
178
+ return await this.sandboxAction(sandboxId, "terminal", { reservationKey });
179
+ }
180
+ async listTools() {
181
+ const result = await this.request("GET", "/v1/tools");
182
+ return result?.tools ?? [];
183
+ }
184
+ async createTool(input) {
185
+ const result = await this.request("POST", "/v1/tools", input);
186
+ if (!result)
187
+ throw new BroodsAccountApiError("POST", "/v1/tools", 404, "Not found");
188
+ return result;
189
+ }
190
+ async getTool(toolId) {
191
+ return await this.request("GET", `/v1/tools/${encodeURIComponent(toolId)}`);
192
+ }
193
+ async updateTool(toolId, patch) {
194
+ return await this.request("PATCH", `/v1/tools/${encodeURIComponent(toolId)}`, patch);
195
+ }
196
+ async deleteTool(toolId) {
197
+ const result = await this.request("DELETE", `/v1/tools/${encodeURIComponent(toolId)}`);
198
+ return result?.deleted ?? false;
199
+ }
200
+ async listPolicies() {
201
+ const result = await this.request("GET", "/v1/policies");
202
+ return result?.policies ?? [];
203
+ }
204
+ async createPolicy(input) {
205
+ const result = await this.request("POST", "/v1/policies", input);
206
+ if (!result)
207
+ throw new BroodsAccountApiError("POST", "/v1/policies", 404, "Not found");
208
+ return result;
209
+ }
210
+ async getPolicy(policyId) {
211
+ return await this.request("GET", `/v1/policies/${encodeURIComponent(policyId)}`);
212
+ }
213
+ async updatePolicy(policyId, patch) {
214
+ return await this.request("PATCH", `/v1/policies/${encodeURIComponent(policyId)}`, patch);
215
+ }
216
+ async deletePolicy(policyId) {
217
+ const result = await this.request("DELETE", `/v1/policies/${encodeURIComponent(policyId)}`);
218
+ return result?.deleted ?? false;
219
+ }
220
+ async listSkills() {
221
+ const result = await this.request("GET", "/v1/skills");
222
+ return result?.skills ?? [];
223
+ }
224
+ async createSkill(input) {
225
+ const result = await this.request("POST", "/v1/skills", input);
226
+ if (!result)
227
+ throw new BroodsAccountApiError("POST", "/v1/skills", 404, "Not found");
228
+ return result;
229
+ }
230
+ async getSkill(skillName) {
231
+ return await this.request("GET", `/v1/skills/${encodeURIComponent(skillName)}`);
232
+ }
233
+ async uploadSkill(skillName, input) {
234
+ const path = `/v1/skills/${encodeURIComponent(skillName)}`;
235
+ const result = await this.request("PUT", path, input);
236
+ if (!result)
237
+ throw new BroodsAccountApiError("PUT", path, 404, "Skill not found");
238
+ return result;
239
+ }
240
+ async deleteSkill(skillName) {
241
+ const result = await this.request("DELETE", `/v1/skills/${encodeURIComponent(skillName)}`);
242
+ return result?.deleted ?? false;
243
+ }
244
+ async sandboxAction(sandboxId, action, body) {
245
+ const path = `/v1/sandboxes/${encodeURIComponent(sandboxId)}/${action}`;
246
+ const result = await this.request("POST", path, body);
247
+ if (!result)
248
+ throw new BroodsAccountApiError("POST", path, 404, "Sandbox not found");
249
+ return result;
250
+ }
251
+ async request(method, path, body) {
252
+ const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
253
+ method,
254
+ headers: {
255
+ Authorization: `Bearer ${this.accountSecret}`,
256
+ ...body !== undefined ? { "Content-Type": "application/json" } : {}
257
+ },
258
+ ...body !== undefined ? { body: JSON.stringify(body) } : {}
259
+ });
260
+ if (response.status === 404)
261
+ return null;
262
+ if (!response.ok) {
263
+ throw new BroodsAccountApiError(method, path, response.status, await response.text());
264
+ }
265
+ return await response.json();
266
+ }
267
+ }
268
+ export {
269
+ BroodsAccountClient,
270
+ BroodsAccountApiError
271
+ };
package/dist/cli/index.js CHANGED
@@ -686,8 +686,56 @@ async function normalizeConfig(resource, projectRoot) {
686
686
  }
687
687
  return rewriteValues(resource.config);
688
688
  }
689
+ var KNOWN_PROVIDER_NAMES = ["google", "openai", "anthropic", "bedrock", "gateway", "minimax", "custom"];
690
+ var KNOWN_PROVIDER_SETTING_KEYS = new Set([
691
+ "apiKey",
692
+ "base_url",
693
+ "baseURL",
694
+ "headers",
695
+ "organization",
696
+ "project",
697
+ "name",
698
+ "region",
699
+ "accessKeyId",
700
+ "secretAccessKey",
701
+ "sessionToken"
702
+ ]);
703
+ function suggestProviderKey(key) {
704
+ const canonical = key.replace(/[^a-z0-9]/gi, "").toLowerCase();
705
+ if (canonical === "baseurl")
706
+ return `"base_url" or "baseURL"`;
707
+ if (canonical === "apikey")
708
+ return `"apiKey"`;
709
+ return "";
710
+ }
711
+ function validateProviderConfig(agentName, provider) {
712
+ if (provider === undefined || provider === null)
713
+ return;
714
+ if (typeof provider !== "object" || Array.isArray(provider)) {
715
+ throw new Error(`Agent "${agentName}" config.provider must be an object`);
716
+ }
717
+ for (const [providerName, settings] of Object.entries(provider)) {
718
+ if (!KNOWN_PROVIDER_NAMES.includes(providerName)) {
719
+ throw new Error(`Agent "${agentName}" config.provider.${providerName} is not a supported provider (expected one of: ${KNOWN_PROVIDER_NAMES.join(", ")})`);
720
+ }
721
+ if (typeof settings !== "object" || settings === null || Array.isArray(settings)) {
722
+ throw new Error(`Agent "${agentName}" config.provider.${providerName} must be an object`);
723
+ }
724
+ const record = settings;
725
+ for (const key of Object.keys(record)) {
726
+ if (KNOWN_PROVIDER_SETTING_KEYS.has(key))
727
+ continue;
728
+ const suggestion = suggestProviderKey(key);
729
+ throw new Error(`Agent "${agentName}" config.provider.${providerName} has unknown option "${key}"${suggestion ? ` \u2014 did you mean ${suggestion}?` : ""}`);
730
+ }
731
+ if (providerName === "custom" && record.base_url === undefined && record.baseURL === undefined) {
732
+ throw new Error(`Agent "${agentName}" config.provider.custom.base_url is required (use "base_url" or "baseURL")`);
733
+ }
734
+ }
735
+ }
689
736
  function normalizeAgentConfig(resource, _projectRoot) {
690
737
  const config = { ...resource.config };
738
+ validateProviderConfig(resource.name, config.provider);
691
739
  const inlineHooks = normalizeInlineAgentHooks(resource.name, config.hooks);
692
740
  if (inlineHooks) {
693
741
  config.hooks = inlineHooks.agentHooksConfig;
@@ -2547,7 +2595,7 @@ function renderStreamPart(part, state) {
2547
2595
  // package.json
2548
2596
  var package_default = {
2549
2597
  name: "broods",
2550
- version: "0.2.0",
2598
+ version: "0.3.0",
2551
2599
  description: "CLI + TypeScript SDK for the broods agent platform.",
2552
2600
  type: "module",
2553
2601
  license: "MIT",
@@ -2574,6 +2622,10 @@ var package_default = {
2574
2622
  ".": {
2575
2623
  types: "./dist/index.d.ts",
2576
2624
  import: "./dist/index.js"
2625
+ },
2626
+ "./account": {
2627
+ types: "./dist/account.d.ts",
2628
+ import: "./dist/account.js"
2577
2629
  }
2578
2630
  },
2579
2631
  main: "./dist/index.js",
@@ -2602,6 +2654,7 @@ var package_default = {
2602
2654
  devDependencies: {
2603
2655
  "@types/bun": "^1.3.14",
2604
2656
  rollup: "^4.53.3",
2657
+ typescript: "^6.0.3",
2605
2658
  "rollup-plugin-dts": "^6.3.0"
2606
2659
  }
2607
2660
  };