broods 0.2.1 → 0.4.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 +89 -0
- package/dist/account.d.ts +802 -0
- package/dist/account.js +290 -0
- package/dist/cli/index.js +414 -157
- package/dist/index.d.ts +530 -80
- package/dist/index.js +419 -25
- package/package.json +12 -8
package/dist/index.js
CHANGED
|
@@ -1,4 +1,288 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// src/account.ts
|
|
3
|
+
var DEFAULT_ACCOUNT_BASE_URL = "https://gateway.broods.app";
|
|
4
|
+
var ACCOUNT_ENV_VAR_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/;
|
|
5
|
+
function envPlaceholder(name) {
|
|
6
|
+
if (!ACCOUNT_ENV_VAR_NAME_PATTERN.test(name) || name.length > 64) {
|
|
7
|
+
throw new Error("envPlaceholder name must match /^[A-Z][A-Z0-9_]*$/ and be at most 64 characters.");
|
|
8
|
+
}
|
|
9
|
+
return `\${${name}}`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
class BroodsAccountApiError extends Error {
|
|
13
|
+
status;
|
|
14
|
+
body;
|
|
15
|
+
constructor(method, path, status, body) {
|
|
16
|
+
super(`${method} ${path} failed with ${status}: ${body}`);
|
|
17
|
+
this.name = "BroodsAccountApiError";
|
|
18
|
+
this.status = status;
|
|
19
|
+
this.body = body;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function envVar(name) {
|
|
23
|
+
return typeof process !== "undefined" ? process?.env?.[name] : undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class BroodsAccountClient {
|
|
27
|
+
baseUrl;
|
|
28
|
+
accountSecret;
|
|
29
|
+
fetchImpl;
|
|
30
|
+
constructor(options = {}) {
|
|
31
|
+
const baseUrl = options.baseUrl ?? envVar("BROODS_BASE_URL") ?? DEFAULT_ACCOUNT_BASE_URL;
|
|
32
|
+
const accountSecret = options.accountSecret ?? envVar("BROODS_ACCOUNT_SECRET");
|
|
33
|
+
if (!accountSecret)
|
|
34
|
+
throw new Error("BroodsAccountClient requires an accountSecret (or BROODS_ACCOUNT_SECRET).");
|
|
35
|
+
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
36
|
+
this.accountSecret = accountSecret;
|
|
37
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
38
|
+
}
|
|
39
|
+
async getAccount() {
|
|
40
|
+
const result = await this.request("GET", "/v1/account");
|
|
41
|
+
if (!result)
|
|
42
|
+
throw new BroodsAccountApiError("GET", "/v1/account", 404, "Account not found");
|
|
43
|
+
return result.account;
|
|
44
|
+
}
|
|
45
|
+
async updateAccount(patch) {
|
|
46
|
+
const result = await this.request("PATCH", "/v1/account", patch);
|
|
47
|
+
return result?.account ?? null;
|
|
48
|
+
}
|
|
49
|
+
async rotateSecret() {
|
|
50
|
+
const result = await this.request("POST", "/v1/account/rotate-secret");
|
|
51
|
+
if (!result)
|
|
52
|
+
throw new BroodsAccountApiError("POST", "/v1/account/rotate-secret", 404, "Account not found");
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
async deleteAccount() {
|
|
56
|
+
const result = await this.request("DELETE", "/v1/account");
|
|
57
|
+
return result ?? { deleted: false };
|
|
58
|
+
}
|
|
59
|
+
webhookUrl(accountId, agentId, channelType) {
|
|
60
|
+
const segments = [accountId, agentId, channelType].map(encodeURIComponent);
|
|
61
|
+
return `${this.baseUrl}/webhooks/${segments.join("/")}`;
|
|
62
|
+
}
|
|
63
|
+
async listAgents() {
|
|
64
|
+
const result = await this.request("GET", "/v1/agents");
|
|
65
|
+
return result?.agents ?? [];
|
|
66
|
+
}
|
|
67
|
+
async createAgent(input) {
|
|
68
|
+
const result = await this.request("POST", "/v1/agents", input);
|
|
69
|
+
if (!result)
|
|
70
|
+
throw new BroodsAccountApiError("POST", "/v1/agents", 404, "Not found");
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
async getAgent(agentId) {
|
|
74
|
+
return await this.request("GET", `/v1/agents/${encodeURIComponent(agentId)}`);
|
|
75
|
+
}
|
|
76
|
+
async updateAgent(agentId, patch) {
|
|
77
|
+
return await this.request("PATCH", `/v1/agents/${encodeURIComponent(agentId)}`, patch);
|
|
78
|
+
}
|
|
79
|
+
async deleteAgent(agentId) {
|
|
80
|
+
const result = await this.request("DELETE", `/v1/agents/${encodeURIComponent(agentId)}`);
|
|
81
|
+
return result?.deleted ?? false;
|
|
82
|
+
}
|
|
83
|
+
async listEnvVars() {
|
|
84
|
+
const result = await this.request("GET", "/v1/env");
|
|
85
|
+
return result?.env ?? [];
|
|
86
|
+
}
|
|
87
|
+
async setEnvVar(name, value) {
|
|
88
|
+
await this.request("PUT", `/v1/env/${encodeURIComponent(name)}`, { value });
|
|
89
|
+
}
|
|
90
|
+
async deleteEnvVar(name) {
|
|
91
|
+
const result = await this.request("DELETE", `/v1/env/${encodeURIComponent(name)}`);
|
|
92
|
+
return result?.deleted ?? false;
|
|
93
|
+
}
|
|
94
|
+
async listCrons() {
|
|
95
|
+
const result = await this.request("GET", "/v1/crons");
|
|
96
|
+
return result?.crons ?? [];
|
|
97
|
+
}
|
|
98
|
+
async createCron(input) {
|
|
99
|
+
const result = await this.request("POST", "/v1/crons", input);
|
|
100
|
+
if (!result)
|
|
101
|
+
throw new BroodsAccountApiError("POST", "/v1/crons", 404, "Not found");
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
async getCron(cronId) {
|
|
105
|
+
return await this.request("GET", `/v1/crons/${encodeURIComponent(cronId)}`);
|
|
106
|
+
}
|
|
107
|
+
async updateCron(cronId, patch) {
|
|
108
|
+
return await this.request("PATCH", `/v1/crons/${encodeURIComponent(cronId)}`, patch);
|
|
109
|
+
}
|
|
110
|
+
async deleteCron(cronId) {
|
|
111
|
+
const result = await this.request("DELETE", `/v1/crons/${encodeURIComponent(cronId)}`);
|
|
112
|
+
return result?.deleted ?? false;
|
|
113
|
+
}
|
|
114
|
+
async listCronRuns(cronId, options = {}) {
|
|
115
|
+
const query = options.limit !== undefined ? `?limit=${options.limit}` : "";
|
|
116
|
+
const result = await this.request("GET", `/v1/crons/${encodeURIComponent(cronId)}/runs${query}`);
|
|
117
|
+
return result?.runs ?? [];
|
|
118
|
+
}
|
|
119
|
+
async listWorkspaces() {
|
|
120
|
+
const result = await this.request("GET", "/v1/workspaces");
|
|
121
|
+
return result?.workspaces ?? [];
|
|
122
|
+
}
|
|
123
|
+
async createWorkspace(input) {
|
|
124
|
+
const result = await this.request("POST", "/v1/workspaces", input);
|
|
125
|
+
if (!result)
|
|
126
|
+
throw new BroodsAccountApiError("POST", "/v1/workspaces", 404, "Not found");
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
async getWorkspace(workspaceId) {
|
|
130
|
+
return await this.request("GET", `/v1/workspaces/${encodeURIComponent(workspaceId)}`);
|
|
131
|
+
}
|
|
132
|
+
async updateWorkspace(workspaceId, patch) {
|
|
133
|
+
return await this.request("PATCH", `/v1/workspaces/${encodeURIComponent(workspaceId)}`, patch);
|
|
134
|
+
}
|
|
135
|
+
async deleteWorkspace(workspaceId) {
|
|
136
|
+
const result = await this.request("DELETE", `/v1/workspaces/${encodeURIComponent(workspaceId)}`);
|
|
137
|
+
return result?.deleted ?? false;
|
|
138
|
+
}
|
|
139
|
+
async listWorkspaceFiles(workspaceId) {
|
|
140
|
+
const result = await this.request("GET", `/v1/workspaces/${encodeURIComponent(workspaceId)}/files`);
|
|
141
|
+
return result?.files ?? [];
|
|
142
|
+
}
|
|
143
|
+
async getWorkspaceFileUrl(workspaceId, path) {
|
|
144
|
+
const query = `?path=${encodeURIComponent(path)}`;
|
|
145
|
+
const result = await this.request("GET", `/v1/workspaces/${encodeURIComponent(workspaceId)}/files${query}`);
|
|
146
|
+
return result?.url ?? null;
|
|
147
|
+
}
|
|
148
|
+
async uploadWorkspaceFile(workspaceId, input) {
|
|
149
|
+
const path = `/v1/workspaces/${encodeURIComponent(workspaceId)}/files`;
|
|
150
|
+
const result = await this.request("POST", path, input);
|
|
151
|
+
if (!result)
|
|
152
|
+
throw new BroodsAccountApiError("POST", path, 404, "Workspace not found");
|
|
153
|
+
return result.file;
|
|
154
|
+
}
|
|
155
|
+
async renameWorkspaceFile(workspaceId, path, newPath) {
|
|
156
|
+
const result = await this.request("PATCH", `/v1/workspaces/${encodeURIComponent(workspaceId)}/files`, { path, newPath });
|
|
157
|
+
return result?.renamed ?? false;
|
|
158
|
+
}
|
|
159
|
+
async deleteWorkspaceFile(workspaceId, path) {
|
|
160
|
+
const result = await this.request("DELETE", `/v1/workspaces/${encodeURIComponent(workspaceId)}/files`, { path });
|
|
161
|
+
return result?.deleted ?? false;
|
|
162
|
+
}
|
|
163
|
+
async listSandboxes() {
|
|
164
|
+
const result = await this.request("GET", "/v1/sandboxes");
|
|
165
|
+
return result?.sandboxes ?? [];
|
|
166
|
+
}
|
|
167
|
+
async createSandbox(input) {
|
|
168
|
+
const result = await this.request("POST", "/v1/sandboxes", input);
|
|
169
|
+
if (!result)
|
|
170
|
+
throw new BroodsAccountApiError("POST", "/v1/sandboxes", 404, "Not found");
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
async getSandbox(sandboxId) {
|
|
174
|
+
return await this.request("GET", `/v1/sandboxes/${encodeURIComponent(sandboxId)}`);
|
|
175
|
+
}
|
|
176
|
+
async updateSandbox(sandboxId, patch) {
|
|
177
|
+
return await this.request("PATCH", `/v1/sandboxes/${encodeURIComponent(sandboxId)}`, patch);
|
|
178
|
+
}
|
|
179
|
+
async deleteSandbox(sandboxId) {
|
|
180
|
+
const result = await this.request("DELETE", `/v1/sandboxes/${encodeURIComponent(sandboxId)}`);
|
|
181
|
+
return result?.deleted ?? false;
|
|
182
|
+
}
|
|
183
|
+
async suspendSandbox(sandboxId, reservationKey) {
|
|
184
|
+
return await this.sandboxAction(sandboxId, "suspend", { reservationKey });
|
|
185
|
+
}
|
|
186
|
+
async resumeSandbox(sandboxId, reservationKey) {
|
|
187
|
+
return await this.sandboxAction(sandboxId, "resume", { reservationKey });
|
|
188
|
+
}
|
|
189
|
+
async terminateSandbox(sandboxId, reservationKey) {
|
|
190
|
+
return await this.sandboxAction(sandboxId, "terminate", { reservationKey });
|
|
191
|
+
}
|
|
192
|
+
async snapshotSandbox(sandboxId, reservationKey, name) {
|
|
193
|
+
return await this.sandboxAction(sandboxId, "snapshot", { reservationKey, name });
|
|
194
|
+
}
|
|
195
|
+
async openSandboxTerminal(sandboxId, reservationKey) {
|
|
196
|
+
return await this.sandboxAction(sandboxId, "terminal", { reservationKey });
|
|
197
|
+
}
|
|
198
|
+
async listTools() {
|
|
199
|
+
const result = await this.request("GET", "/v1/tools");
|
|
200
|
+
return result?.tools ?? [];
|
|
201
|
+
}
|
|
202
|
+
async createTool(input) {
|
|
203
|
+
const result = await this.request("POST", "/v1/tools", input);
|
|
204
|
+
if (!result)
|
|
205
|
+
throw new BroodsAccountApiError("POST", "/v1/tools", 404, "Not found");
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
async getTool(toolId) {
|
|
209
|
+
return await this.request("GET", `/v1/tools/${encodeURIComponent(toolId)}`);
|
|
210
|
+
}
|
|
211
|
+
async updateTool(toolId, patch) {
|
|
212
|
+
return await this.request("PATCH", `/v1/tools/${encodeURIComponent(toolId)}`, patch);
|
|
213
|
+
}
|
|
214
|
+
async deleteTool(toolId) {
|
|
215
|
+
const result = await this.request("DELETE", `/v1/tools/${encodeURIComponent(toolId)}`);
|
|
216
|
+
return result?.deleted ?? false;
|
|
217
|
+
}
|
|
218
|
+
async listPolicies() {
|
|
219
|
+
const result = await this.request("GET", "/v1/policies");
|
|
220
|
+
return result?.policies ?? [];
|
|
221
|
+
}
|
|
222
|
+
async createPolicy(input) {
|
|
223
|
+
const result = await this.request("POST", "/v1/policies", input);
|
|
224
|
+
if (!result)
|
|
225
|
+
throw new BroodsAccountApiError("POST", "/v1/policies", 404, "Not found");
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
async getPolicy(policyId) {
|
|
229
|
+
return await this.request("GET", `/v1/policies/${encodeURIComponent(policyId)}`);
|
|
230
|
+
}
|
|
231
|
+
async updatePolicy(policyId, patch) {
|
|
232
|
+
return await this.request("PATCH", `/v1/policies/${encodeURIComponent(policyId)}`, patch);
|
|
233
|
+
}
|
|
234
|
+
async deletePolicy(policyId) {
|
|
235
|
+
const result = await this.request("DELETE", `/v1/policies/${encodeURIComponent(policyId)}`);
|
|
236
|
+
return result?.deleted ?? false;
|
|
237
|
+
}
|
|
238
|
+
async listSkills() {
|
|
239
|
+
const result = await this.request("GET", "/v1/skills");
|
|
240
|
+
return result?.skills ?? [];
|
|
241
|
+
}
|
|
242
|
+
async createSkill(input) {
|
|
243
|
+
const result = await this.request("POST", "/v1/skills", input);
|
|
244
|
+
if (!result)
|
|
245
|
+
throw new BroodsAccountApiError("POST", "/v1/skills", 404, "Not found");
|
|
246
|
+
return result;
|
|
247
|
+
}
|
|
248
|
+
async getSkill(skillName) {
|
|
249
|
+
return await this.request("GET", `/v1/skills/${encodeURIComponent(skillName)}`);
|
|
250
|
+
}
|
|
251
|
+
async uploadSkill(skillName, input) {
|
|
252
|
+
const path = `/v1/skills/${encodeURIComponent(skillName)}`;
|
|
253
|
+
const result = await this.request("PUT", path, input);
|
|
254
|
+
if (!result)
|
|
255
|
+
throw new BroodsAccountApiError("PUT", path, 404, "Skill not found");
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
async deleteSkill(skillName) {
|
|
259
|
+
const result = await this.request("DELETE", `/v1/skills/${encodeURIComponent(skillName)}`);
|
|
260
|
+
return result?.deleted ?? false;
|
|
261
|
+
}
|
|
262
|
+
async sandboxAction(sandboxId, action, body) {
|
|
263
|
+
const path = `/v1/sandboxes/${encodeURIComponent(sandboxId)}/${action}`;
|
|
264
|
+
const result = await this.request("POST", path, body);
|
|
265
|
+
if (!result)
|
|
266
|
+
throw new BroodsAccountApiError("POST", path, 404, "Sandbox not found");
|
|
267
|
+
return result;
|
|
268
|
+
}
|
|
269
|
+
async request(method, path, body) {
|
|
270
|
+
const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
271
|
+
method,
|
|
272
|
+
headers: {
|
|
273
|
+
Authorization: `Bearer ${this.accountSecret}`,
|
|
274
|
+
...body !== undefined ? { "Content-Type": "application/json" } : {}
|
|
275
|
+
},
|
|
276
|
+
...body !== undefined ? { body: JSON.stringify(body) } : {}
|
|
277
|
+
});
|
|
278
|
+
if (response.status === 404)
|
|
279
|
+
return null;
|
|
280
|
+
if (!response.ok) {
|
|
281
|
+
throw new BroodsAccountApiError(method, path, response.status, await response.text());
|
|
282
|
+
}
|
|
283
|
+
return await response.json();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
2
286
|
// src/run-input.ts
|
|
3
287
|
function resolveRunEvents(input) {
|
|
4
288
|
if (input.events && input.input !== undefined) {
|
|
@@ -71,6 +355,9 @@ import { dirname, join } from "path";
|
|
|
71
355
|
var PROJECT_DIR = "broods";
|
|
72
356
|
var GENERATED_DIR = "_generated";
|
|
73
357
|
var USER_CONFIG_PATH = join(homedir(), ".broods", "config.json");
|
|
358
|
+
var BROODS_APEX_DOMAIN = "broods.app";
|
|
359
|
+
var DASHBOARD_HOST_PREFIX = "dashboard";
|
|
360
|
+
var GATEWAY_HOST_PREFIX = "gateway";
|
|
74
361
|
async function readStoredAuth() {
|
|
75
362
|
const envToken = process.env.BROODS_TOKEN;
|
|
76
363
|
const envConvexUrl = process.env.BROODS_BASE_URL;
|
|
@@ -98,6 +385,20 @@ async function writeStoredAuth(config) {
|
|
|
98
385
|
function stripTrailingSlash(value) {
|
|
99
386
|
return value.replace(/\/+$/, "");
|
|
100
387
|
}
|
|
388
|
+
function gatewayUrlForDashboard(dashboardUrl) {
|
|
389
|
+
try {
|
|
390
|
+
const url = new URL(dashboardUrl);
|
|
391
|
+
if (!url.hostname.startsWith(`${DASHBOARD_HOST_PREFIX}.`))
|
|
392
|
+
return;
|
|
393
|
+
const rest = url.hostname.slice(DASHBOARD_HOST_PREFIX.length + 1);
|
|
394
|
+
if (rest !== BROODS_APEX_DOMAIN && !rest.endsWith(`.${BROODS_APEX_DOMAIN}`))
|
|
395
|
+
return;
|
|
396
|
+
url.hostname = `${GATEWAY_HOST_PREFIX}.${rest}`;
|
|
397
|
+
return stripTrailingSlash(url.origin);
|
|
398
|
+
} catch {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
101
402
|
|
|
102
403
|
// src/runtime-config.ts
|
|
103
404
|
var envFileCache = new Map;
|
|
@@ -182,6 +483,15 @@ function readStoredAuthSync() {
|
|
|
182
483
|
// src/client.ts
|
|
183
484
|
var DEFAULT_CORE_BASE_URL = "https://gateway.broods.app";
|
|
184
485
|
|
|
486
|
+
class IngressAcceptedError extends Error {
|
|
487
|
+
accepted;
|
|
488
|
+
constructor(accepted) {
|
|
489
|
+
super(`Ingress ${accepted.eventId} was accepted asynchronously with status ${accepted.status ?? "queued"}`);
|
|
490
|
+
this.accepted = accepted;
|
|
491
|
+
this.name = "IngressAcceptedError";
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
185
495
|
class BroodsClient {
|
|
186
496
|
baseUrl;
|
|
187
497
|
apiKey;
|
|
@@ -240,6 +550,9 @@ class BroodsClient {
|
|
|
240
550
|
const response = await this.openStream(body, targetUrl);
|
|
241
551
|
if (!response.ok)
|
|
242
552
|
throw new Error(`Run failed: ${response.status} ${await response.text()}`);
|
|
553
|
+
if (response.status === 202) {
|
|
554
|
+
throw new IngressAcceptedError(normalizeAsyncAccepted(await response.json(), body));
|
|
555
|
+
}
|
|
243
556
|
if (!response.body)
|
|
244
557
|
throw new Error("Run response has no body");
|
|
245
558
|
for await (const data of readSseStream(response.body)) {
|
|
@@ -297,7 +610,7 @@ class BroodsClient {
|
|
|
297
610
|
if (options.signal?.aborted)
|
|
298
611
|
throw new Error("Async status polling aborted.");
|
|
299
612
|
const payload = await this.getAsyncStatus(status, options);
|
|
300
|
-
if (payload.status === "awaiting_approval" || payload.status === "completed" || payload.status === "failed" || payload.status === "not_found") {
|
|
613
|
+
if (payload.status === "awaiting_approval" || payload.status === "completed" || payload.status === "failed" || payload.status === "expired" || payload.status === "not_found") {
|
|
301
614
|
return payload;
|
|
302
615
|
}
|
|
303
616
|
await sleep(intervalMs, options.signal);
|
|
@@ -444,6 +757,8 @@ function directRunBody(input, prefix) {
|
|
|
444
757
|
eventId,
|
|
445
758
|
conversationKey: input.conversationKey ?? eventId,
|
|
446
759
|
events: resolveRunEvents(input),
|
|
760
|
+
...input.mode !== undefined ? { mode: input.mode } : {},
|
|
761
|
+
...input.idempotencyKey !== undefined ? { idempotencyKey: input.idempotencyKey } : {},
|
|
447
762
|
...input.system !== undefined ? { system: input.system } : {},
|
|
448
763
|
...input.model !== undefined ? { model: input.model } : {}
|
|
449
764
|
};
|
|
@@ -459,11 +774,16 @@ function normalizeAsyncAccepted(payload, requestBody) {
|
|
|
459
774
|
const status = parseStatusUrl(statusUrl);
|
|
460
775
|
if (!status.statusId)
|
|
461
776
|
throw new Error("Async response statusUrl missing status id");
|
|
777
|
+
const eventId = typeof payload.eventId === "string" ? payload.eventId : status.statusId;
|
|
778
|
+
const acceptedStatus = payload.status;
|
|
779
|
+
const requestedMode = payload.requestedMode;
|
|
462
780
|
return {
|
|
463
781
|
statusUrl,
|
|
464
782
|
statusId: status.statusId,
|
|
465
|
-
eventId
|
|
466
|
-
agentId: status.agentId ?? requestBody.agentId
|
|
783
|
+
eventId,
|
|
784
|
+
agentId: status.agentId ?? requestBody.agentId,
|
|
785
|
+
...acceptedStatus === "accepted" || acceptedStatus === "queued" || acceptedStatus === "applied" || acceptedStatus === "processing" ? { status: acceptedStatus } : {},
|
|
786
|
+
...requestedMode === "reject" || requestedMode === "followup" || requestedMode === "collect" || requestedMode === "steer" ? { requestedMode } : {}
|
|
467
787
|
};
|
|
468
788
|
}
|
|
469
789
|
function parseStatusUrl(statusUrl) {
|
|
@@ -596,6 +916,8 @@ class BroodsWebSocketClient {
|
|
|
596
916
|
events: resolveRunEvents(input),
|
|
597
917
|
sessionId: input.sessionId,
|
|
598
918
|
...input.eventId !== undefined ? { eventId: input.eventId } : {},
|
|
919
|
+
...input.mode !== undefined ? { mode: input.mode } : {},
|
|
920
|
+
...input.idempotencyKey !== undefined ? { idempotencyKey: input.idempotencyKey } : {},
|
|
599
921
|
...input.system !== undefined ? { system: input.system } : {},
|
|
600
922
|
...input.model !== undefined ? { model: input.model } : {}
|
|
601
923
|
}));
|
|
@@ -606,16 +928,17 @@ class BroodsWebSocketClient {
|
|
|
606
928
|
const payload = parseServerMessage(event.data);
|
|
607
929
|
if (!payload)
|
|
608
930
|
return;
|
|
609
|
-
|
|
610
|
-
|
|
931
|
+
const effective = unwrapServerMessage(payload, handlers);
|
|
932
|
+
handlers.onMessage?.(effective);
|
|
933
|
+
switch (effective.type) {
|
|
611
934
|
case "meta":
|
|
612
|
-
handlers.onMeta?.(
|
|
935
|
+
handlers.onMeta?.(effective);
|
|
613
936
|
break;
|
|
614
937
|
case "done":
|
|
615
938
|
finish();
|
|
616
939
|
break;
|
|
617
940
|
case "error":
|
|
618
|
-
fail(new Error(formatWireError(
|
|
941
|
+
fail(new Error(formatWireError(effective.error)));
|
|
619
942
|
break;
|
|
620
943
|
}
|
|
621
944
|
};
|
|
@@ -631,6 +954,57 @@ class BroodsWebSocketClient {
|
|
|
631
954
|
};
|
|
632
955
|
return {
|
|
633
956
|
url,
|
|
957
|
+
sendControl(message) {
|
|
958
|
+
if (socket.readyState !== WS_OPEN)
|
|
959
|
+
throw new Error("WebSocket is not open");
|
|
960
|
+
socket.send(JSON.stringify({ type: "control", ...message }));
|
|
961
|
+
},
|
|
962
|
+
close
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
attach(input, handlers = {}) {
|
|
966
|
+
const WebSocketImpl = this.resolveWebSocket();
|
|
967
|
+
const url = this.buildUrl(input);
|
|
968
|
+
const socket = new WebSocketImpl(url);
|
|
969
|
+
let closed = false;
|
|
970
|
+
const close = (code = 1000, reason = "client closed") => {
|
|
971
|
+
if (closed)
|
|
972
|
+
return;
|
|
973
|
+
closed = true;
|
|
974
|
+
if (socket.readyState === WS_OPEN || socket.readyState === WS_CONNECTING)
|
|
975
|
+
socket.close(code, reason);
|
|
976
|
+
};
|
|
977
|
+
socket.onopen = () => socket.send(JSON.stringify({
|
|
978
|
+
type: "attach",
|
|
979
|
+
...input,
|
|
980
|
+
signal: undefined,
|
|
981
|
+
agent: undefined
|
|
982
|
+
}));
|
|
983
|
+
socket.onmessage = (event) => {
|
|
984
|
+
if (typeof event.data !== "string")
|
|
985
|
+
return;
|
|
986
|
+
const payload = parseServerMessage(event.data);
|
|
987
|
+
if (!payload)
|
|
988
|
+
return;
|
|
989
|
+
const effective = unwrapServerMessage(payload, handlers);
|
|
990
|
+
handlers.onMessage?.(effective);
|
|
991
|
+
if (effective.type === "done")
|
|
992
|
+
handlers.onDone?.();
|
|
993
|
+
if (effective.type === "error")
|
|
994
|
+
handlers.onError?.(new Error(formatWireError(effective.error)));
|
|
995
|
+
};
|
|
996
|
+
socket.onerror = () => handlers.onError?.(webSocketAccessError(this.baseUrl));
|
|
997
|
+
socket.onclose = (event) => {
|
|
998
|
+
if (!closed && event.code !== 1000)
|
|
999
|
+
handlers.onError?.(new Error(event.reason || "WebSocket connection closed."));
|
|
1000
|
+
};
|
|
1001
|
+
return {
|
|
1002
|
+
url,
|
|
1003
|
+
sendControl(message) {
|
|
1004
|
+
if (socket.readyState !== WS_OPEN)
|
|
1005
|
+
throw new Error("WebSocket is not open");
|
|
1006
|
+
socket.send(JSON.stringify({ type: "control", ...message }));
|
|
1007
|
+
},
|
|
634
1008
|
close
|
|
635
1009
|
};
|
|
636
1010
|
}
|
|
@@ -723,6 +1097,13 @@ function normalizeWebSocketServiceUrl(value) {
|
|
|
723
1097
|
function webSocketAccessError(baseUrl) {
|
|
724
1098
|
return new Error(`Cannot access the WebSocket service at ${baseUrl}.`);
|
|
725
1099
|
}
|
|
1100
|
+
function unwrapServerMessage(payload, handlers) {
|
|
1101
|
+
if (payload.type !== "output")
|
|
1102
|
+
return payload;
|
|
1103
|
+
const output = payload;
|
|
1104
|
+
handlers.onOutput?.(output);
|
|
1105
|
+
return output.data;
|
|
1106
|
+
}
|
|
726
1107
|
function parseServerMessage(data) {
|
|
727
1108
|
try {
|
|
728
1109
|
const value = JSON.parse(data);
|
|
@@ -743,25 +1124,27 @@ function formatWireError(error) {
|
|
|
743
1124
|
var RESOURCE_MARKER = Symbol.for("broods.resource");
|
|
744
1125
|
var CONFIG_MARKER = Symbol.for("broods.config");
|
|
745
1126
|
var CHANNEL_MARKER = Symbol.for("broods.channel");
|
|
746
|
-
var
|
|
1127
|
+
var ENV_NAME_PATTERN = /^[A-Z][A-Z0-9_]{0,63}$/;
|
|
1128
|
+
var env = new Proxy((name) => {
|
|
1129
|
+
if (!ENV_NAME_PATTERN.test(name)) {
|
|
1130
|
+
throw new Error("env name must match /^[A-Z][A-Z0-9_]*$/ and be at most 64 characters.");
|
|
1131
|
+
}
|
|
747
1132
|
return { __beeblastEnv: true, name };
|
|
748
1133
|
}, {
|
|
749
1134
|
get(target, property, receiver) {
|
|
750
|
-
if (typeof property === "string")
|
|
751
|
-
|
|
1135
|
+
if (typeof property === "string" && ENV_NAME_PATTERN.test(property)) {
|
|
1136
|
+
throw new Error(`env.${property} is not supported; use env("${property}")`);
|
|
1137
|
+
}
|
|
752
1138
|
return Reflect.get(target, property, receiver);
|
|
753
1139
|
}
|
|
754
1140
|
});
|
|
755
|
-
function defineResource(kind,
|
|
756
|
-
if (input.config === undefined) {
|
|
757
|
-
throw new Error(`Resource "${input.name}" must include config`);
|
|
758
|
-
}
|
|
1141
|
+
function defineResource(kind, name, description, config) {
|
|
759
1142
|
return {
|
|
760
1143
|
[RESOURCE_MARKER]: true,
|
|
761
1144
|
kind,
|
|
762
|
-
name
|
|
763
|
-
...
|
|
764
|
-
config
|
|
1145
|
+
name,
|
|
1146
|
+
...description ? { description } : {},
|
|
1147
|
+
config
|
|
765
1148
|
};
|
|
766
1149
|
}
|
|
767
1150
|
function defineChannel(type, config) {
|
|
@@ -799,25 +1182,32 @@ function defineBroods(config) {
|
|
|
799
1182
|
return { [CONFIG_MARKER]: true, config };
|
|
800
1183
|
}
|
|
801
1184
|
function defineAgent(input) {
|
|
802
|
-
|
|
1185
|
+
const { name, description, ...config } = input;
|
|
1186
|
+
return defineResource("agent", name, description, config);
|
|
803
1187
|
}
|
|
804
1188
|
function defineWorkspace(input) {
|
|
805
|
-
|
|
1189
|
+
const { name, description, ...config } = input;
|
|
1190
|
+
return defineResource("workspace", name, description, config);
|
|
806
1191
|
}
|
|
807
1192
|
function defineSandbox(input) {
|
|
808
|
-
|
|
1193
|
+
const { name, description, ...config } = input;
|
|
1194
|
+
return defineResource("sandbox", name, description, config);
|
|
809
1195
|
}
|
|
810
1196
|
function defineSkill(input) {
|
|
811
|
-
|
|
1197
|
+
const { name, description, ...config } = input;
|
|
1198
|
+
return defineResource("skill", name, description, config);
|
|
812
1199
|
}
|
|
813
1200
|
function defineTool(input) {
|
|
814
|
-
|
|
1201
|
+
const { name, ...config } = input;
|
|
1202
|
+
return defineResource("tool", name, undefined, config);
|
|
815
1203
|
}
|
|
816
1204
|
function definePolicy(input) {
|
|
817
|
-
|
|
1205
|
+
const { name, description, ...config } = input;
|
|
1206
|
+
return defineResource("policy", name, description, config);
|
|
818
1207
|
}
|
|
819
1208
|
function defineCron(input) {
|
|
820
|
-
|
|
1209
|
+
const { name, description, ...config } = input;
|
|
1210
|
+
return defineResource("cron", name, description, config);
|
|
821
1211
|
}
|
|
822
1212
|
function isResource(value) {
|
|
823
1213
|
return Boolean(value && typeof value === "object" && value[RESOURCE_MARKER]);
|
|
@@ -837,6 +1227,7 @@ export {
|
|
|
837
1227
|
isObservabilityClientMessage,
|
|
838
1228
|
isChannelDefinition,
|
|
839
1229
|
isBroodsConfig,
|
|
1230
|
+
envPlaceholder,
|
|
840
1231
|
env,
|
|
841
1232
|
defineZaloChannel,
|
|
842
1233
|
defineWorkspace,
|
|
@@ -855,7 +1246,10 @@ export {
|
|
|
855
1246
|
BroodsWebSocketClient as WebsocketClient,
|
|
856
1247
|
BroodsWebSocketClient as WebSocketClient,
|
|
857
1248
|
MAX_OBSERVABILITY_BACKFILL,
|
|
1249
|
+
IngressAcceptedError,
|
|
858
1250
|
DEFAULT_CORE_BASE_URL,
|
|
859
1251
|
BroodsWebSocketClient,
|
|
860
|
-
BroodsClient
|
|
1252
|
+
BroodsClient,
|
|
1253
|
+
BroodsAccountClient,
|
|
1254
|
+
BroodsAccountApiError
|
|
861
1255
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "broods",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "CLI + TypeScript SDK for the broods agent platform.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
".": {
|
|
28
28
|
"types": "./dist/index.d.ts",
|
|
29
29
|
"import": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./account": {
|
|
32
|
+
"types": "./dist/account.d.ts",
|
|
33
|
+
"import": "./dist/account.js"
|
|
30
34
|
}
|
|
31
35
|
},
|
|
32
36
|
"main": "./dist/index.js",
|
|
@@ -46,16 +50,16 @@
|
|
|
46
50
|
"node": ">=22"
|
|
47
51
|
},
|
|
48
52
|
"dependencies": {
|
|
49
|
-
"@chat-adapter/discord": "^4.
|
|
50
|
-
"@chat-adapter/github": "^4.
|
|
51
|
-
"@chat-adapter/slack": "^4.
|
|
52
|
-
"@chat-adapter/telegram": "^4.
|
|
53
|
-
"ai": "^7.0.
|
|
53
|
+
"@chat-adapter/discord": "^4.35.0",
|
|
54
|
+
"@chat-adapter/github": "^4.35.0",
|
|
55
|
+
"@chat-adapter/slack": "^4.35.0",
|
|
56
|
+
"@chat-adapter/telegram": "^4.35.0",
|
|
57
|
+
"ai": "^7.0.37"
|
|
54
58
|
},
|
|
55
59
|
"devDependencies": {
|
|
56
60
|
"@types/bun": "^1.3.14",
|
|
57
|
-
"rollup": "^4.
|
|
61
|
+
"rollup": "^4.62.2",
|
|
58
62
|
"typescript": "^6.0.3",
|
|
59
|
-
"rollup-plugin-dts": "^6.
|
|
63
|
+
"rollup-plugin-dts": "^6.4.1"
|
|
60
64
|
}
|
|
61
65
|
}
|