broods 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,1255 +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
- }
286
- // src/run-input.ts
287
- function resolveRunEvents(input) {
288
- if (input.events && input.input !== undefined) {
289
- throw new Error("Run input accepts either `input` or `events`, not both");
290
- }
291
- if (input.events && input.events.length > 0)
292
- return input.events;
293
- if (typeof input.input === "string") {
294
- return [{ role: "user", content: [{ type: "text", text: input.input }] }];
295
- }
296
- throw new Error("Run input requires `input` (string) or a non-empty `events` array");
297
- }
298
- // src/stream.ts
299
- async function* readSseStream(body) {
300
- const reader = body.getReader();
301
- const decoder = new TextDecoder;
302
- let buffer = "";
303
- try {
304
- while (true) {
305
- const { done, value } = await reader.read();
306
- if (done)
307
- break;
308
- buffer += decoder.decode(value, { stream: true });
309
- const lines = buffer.split(`
310
- `);
311
- buffer = lines.pop() ?? "";
312
- for (const line of lines) {
313
- if (line.startsWith("data: "))
314
- yield line.slice(6);
315
- }
316
- }
317
- } finally {
318
- reader.releaseLock();
319
- }
320
- }
321
- // src/observability-contracts.ts
322
- var MAX_OBSERVABILITY_BACKFILL = 500;
323
- function isObservabilityClientMessage(v) {
324
- if (typeof v !== "object" || v === null)
325
- return false;
326
- const msg = v;
327
- if (msg["type"] === "subscribe") {
328
- const stream = msg["stream"];
329
- if (stream !== "logs" && stream !== "traces")
330
- return false;
331
- const backfill = msg["backfill"];
332
- if (backfill !== undefined && (typeof backfill !== "number" || !Number.isSafeInteger(backfill) || backfill < 0 || backfill > MAX_OBSERVABILITY_BACKFILL))
333
- return false;
334
- if (msg["liveOnly"] !== undefined && typeof msg["liveOnly"] !== "boolean")
335
- return false;
336
- const minLevel = msg["minLevel"];
337
- if (minLevel !== undefined && minLevel !== "INFO" && minLevel !== "WARN" && minLevel !== "ERROR")
338
- return false;
339
- return true;
340
- }
341
- if (msg["type"] === "unsubscribe") {
342
- const stream = msg["stream"];
343
- return stream === "logs" || stream === "traces";
344
- }
345
- return false;
346
- }
347
- // src/runtime-config.ts
348
- import { existsSync, readFileSync, statSync } from "fs";
349
- import { join as join2, resolve } from "path";
350
-
351
- // src/config.ts
352
- import { mkdir, readFile, writeFile } from "fs/promises";
353
- import { homedir } from "os";
354
- import { dirname, join } from "path";
355
- var PROJECT_DIR = "broods";
356
- var GENERATED_DIR = "_generated";
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";
361
- async function readStoredAuth() {
362
- const envToken = process.env.BROODS_TOKEN;
363
- const envConvexUrl = process.env.BROODS_BASE_URL;
364
- if (envToken && envConvexUrl) {
365
- return {
366
- baseUrl: stripTrailingSlash(envConvexUrl),
367
- token: envToken,
368
- createdAt: new Date().toISOString()
369
- };
370
- }
371
- try {
372
- const stored = JSON.parse(await readFile(USER_CONFIG_PATH, "utf8"));
373
- if (typeof stored.baseUrl !== "string" || !stored.baseUrl)
374
- return null;
375
- return stored;
376
- } catch {
377
- return null;
378
- }
379
- }
380
- async function writeStoredAuth(config) {
381
- await mkdir(dirname(USER_CONFIG_PATH), { recursive: true });
382
- await writeFile(USER_CONFIG_PATH, `${JSON.stringify(config, null, 2)}
383
- `, "utf8");
384
- }
385
- function stripTrailingSlash(value) {
386
- return value.replace(/\/+$/, "");
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
- }
402
-
403
- // src/runtime-config.ts
404
- var envFileCache = new Map;
405
- var realEnvSnapshot = new Set(Object.keys(process.env));
406
- var loadedFromFiles = new Set;
407
- function loadBroodsRuntimeConfig(cwd = process.cwd()) {
408
- loadEnvFiles(cwd);
409
- const stored = readStoredAuthSync();
410
- return {
411
- dashboardUrl: process.env.BROODS_DASHBOARD_URL ?? stored?.dashboardUrl,
412
- baseUrl: process.env.BROODS_BASE_URL ?? stored?.baseUrl,
413
- token: process.env.BROODS_TOKEN ?? stored?.token,
414
- project: process.env.BROODS_PROJECT,
415
- environment: process.env.BROODS_ENVIRONMENT
416
- };
417
- }
418
- function loadEnvFiles(cwd) {
419
- const root = resolve(cwd);
420
- const forceReload = process.env.BROODS_RELOAD_ENV === "1";
421
- for (const file of [".env", ".env.local"]) {
422
- const path = join2(root, file);
423
- if (!existsSync(path)) {
424
- envFileCache.delete(path);
425
- continue;
426
- }
427
- const mtimeMs = statSync(path).mtimeMs;
428
- const cached = envFileCache.get(path);
429
- if (cached && cached.mtimeMs === mtimeMs)
430
- continue;
431
- envFileCache.set(path, { mtimeMs });
432
- for (const [key, value] of Object.entries(parseEnv(readFileSync(path, "utf8")))) {
433
- if (forceReload || !realEnvSnapshot.has(key) || loadedFromFiles.has(key)) {
434
- process.env[key] = value;
435
- loadedFromFiles.add(key);
436
- }
437
- }
438
- }
439
- }
440
- function parseEnv(source) {
441
- const values = {};
442
- for (const rawLine of source.split(/\r?\n/)) {
443
- const line = rawLine.trim();
444
- if (!line || line.startsWith("#"))
445
- continue;
446
- const assignment = line.startsWith("export ") ? line.slice("export ".length).trim() : line;
447
- const eq = assignment.indexOf("=");
448
- if (eq <= 0)
449
- continue;
450
- const key = assignment.slice(0, eq).trim();
451
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
452
- continue;
453
- values[key] = unquoteEnvValue(assignment.slice(eq + 1).trim());
454
- }
455
- return values;
456
- }
457
- function unquoteEnvValue(value) {
458
- if (value.startsWith('"') && value.endsWith('"')) {
459
- return value.slice(1, -1).replace(/\\n/g, `
460
- `).replace(/\\"/g, '"');
461
- }
462
- if (value.startsWith("'") && value.endsWith("'")) {
463
- return value.slice(1, -1);
464
- }
465
- const commentIndex = value.indexOf(" #");
466
- return commentIndex >= 0 ? value.slice(0, commentIndex).trimEnd() : value;
467
- }
468
- function readStoredAuthSync() {
469
- try {
470
- const value = JSON.parse(readFileSync(USER_CONFIG_PATH, "utf8"));
471
- if (typeof value.baseUrl !== "string" || typeof value.token !== "string")
472
- return null;
473
- return {
474
- baseUrl: stripTrailingSlash(value.baseUrl),
475
- ...typeof value.dashboardUrl === "string" ? { dashboardUrl: stripTrailingSlash(value.dashboardUrl) } : {},
476
- token: value.token
477
- };
478
- } catch {
479
- return null;
480
- }
481
- }
482
-
483
- // src/client.ts
484
- var DEFAULT_CORE_BASE_URL = "https://gateway.broods.app";
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
-
495
- class BroodsClient {
496
- baseUrl;
497
- apiKey;
498
- fetchImpl;
499
- constructor(options = {}) {
500
- loadBroodsRuntimeConfig();
501
- this.baseUrl = normalizeHttpServiceUrl(options.baseUrl || options.host || process.env.BROODS_BASE_URL || process.env.BROODS_HOST || DEFAULT_CORE_BASE_URL);
502
- this.apiKey = options.apiKey || process.env.BROODS_API_KEY || undefined;
503
- this.fetchImpl = options.fetch ?? fetch;
504
- }
505
- channelWebhookUrl(ref) {
506
- return `${this.baseUrl}${ref.webhookPath.startsWith("/") ? "" : "/"}${ref.webhookPath}`;
507
- }
508
- agent(refOrName, agentId) {
509
- if (typeof refOrName === "string") {
510
- const name = refOrName;
511
- const id = agentId ?? "";
512
- if (!id)
513
- throw new Error(`Agent ${name} is missing a generated id. Run broods dev --once or broods deploy to sync it first.`);
514
- return {
515
- id,
516
- run: (input) => this.run({ ...input, agentName: name, agentId: id }),
517
- runAsync: (input) => this.runAsync({ ...input, agentName: name, agentId: id }),
518
- stream: (input) => this.stream({ ...input, agentName: name, agentId: id })
519
- };
520
- }
521
- const ref = refOrName;
522
- if (!ref.id)
523
- throw new Error(`Agent ${ref.name} is missing a generated id. Run broods dev --once or broods deploy to sync it first.`);
524
- return {
525
- id: ref.id,
526
- run: (input) => this.run(ref, input),
527
- runAsync: (input) => this.runAsync(ref, input),
528
- stream: (input) => this.stream(ref, input)
529
- };
530
- }
531
- async run(refOrInput, maybeInput) {
532
- const events = [];
533
- let text = "";
534
- const stream = maybeInput ? this.stream(refOrInput, maybeInput) : this.stream(refOrInput);
535
- for await (const part of stream) {
536
- events.push(part);
537
- if (part.type === "text-delta")
538
- text += part.text;
539
- }
540
- return { text, events };
541
- }
542
- async* stream(refOrInput, maybeInput) {
543
- const input = maybeInput ? {
544
- ...maybeInput,
545
- agentId: refOrInput.id,
546
- agentName: refOrInput.name
547
- } : refOrInput;
548
- const body = directRunBody(input, "cli");
549
- const targetUrl = maybeInput ? this.scopedUrl(refOrInput) : this.baseUrl;
550
- const response = await this.openStream(body, targetUrl);
551
- if (!response.ok)
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
- }
556
- if (!response.body)
557
- throw new Error("Run response has no body");
558
- for await (const data of readSseStream(response.body)) {
559
- let part;
560
- try {
561
- part = JSON.parse(data);
562
- } catch {
563
- continue;
564
- }
565
- if (part.type === "error")
566
- throw new Error(`Agent run failed: ${formatStreamError(part.error)}`);
567
- yield part;
568
- }
569
- }
570
- async runAsync(refOrInput, maybeInput) {
571
- const input = maybeInput ? {
572
- ...maybeInput,
573
- agentId: refOrInput.id,
574
- agentName: refOrInput.name
575
- } : refOrInput;
576
- const body = directRunBody(input, "async");
577
- const targetUrl = maybeInput ? this.scopedUrl(refOrInput, "/async") : `${this.baseUrl}/async`;
578
- const response = await this.fetchJson(targetUrl, {
579
- method: "POST",
580
- headers: this.apiKeyHeaders(),
581
- body: JSON.stringify(body)
582
- });
583
- if (response.status !== 202) {
584
- throw new Error(`Async run failed: ${response.status} ${await responseErrorDetails(response, "202 JSON")}`);
585
- }
586
- const accepted = normalizeAsyncAccepted(await response.json(), body);
587
- return {
588
- ...accepted,
589
- conversationKey: body.conversationKey,
590
- poll: () => this.getAsyncStatus(accepted),
591
- wait: (options) => this.waitForAsyncStatus(accepted, options)
592
- };
593
- }
594
- async getAsyncStatus(status, options = {}) {
595
- const statusUrl = this.resolveStatusUrl(status, options);
596
- const response = await this.fetchJson(statusUrl, {
597
- method: "GET",
598
- headers: this.apiKeyHeaders()
599
- });
600
- if (response.status === 404)
601
- return { status: "not_found" };
602
- if (!response.ok)
603
- throw new Error(`Status check failed: ${response.status} ${await response.text()}`);
604
- return await response.json();
605
- }
606
- async waitForAsyncStatus(status, options = {}) {
607
- const deadline = Date.now() + (options.timeoutMs ?? 180000);
608
- const intervalMs = options.intervalMs ?? 2000;
609
- while (Date.now() < deadline) {
610
- if (options.signal?.aborted)
611
- throw new Error("Async status polling aborted.");
612
- const payload = await this.getAsyncStatus(status, options);
613
- if (payload.status === "awaiting_approval" || payload.status === "completed" || payload.status === "failed" || payload.status === "expired" || payload.status === "not_found") {
614
- return payload;
615
- }
616
- await sleep(intervalMs, options.signal);
617
- }
618
- throw new Error("Polling timeout");
619
- }
620
- async createCron(input) {
621
- const response = await this.fetchJson(`${this.baseUrl}/v1/crons`, {
622
- method: "POST",
623
- headers: this.apiKeyHeaders(),
624
- body: JSON.stringify(resolveCronInput(input))
625
- });
626
- if (response.status !== 201)
627
- throw new Error(`Create cron job failed: ${response.status} ${await cronErrorDetails(response)}`);
628
- return await response.json();
629
- }
630
- async listCrons() {
631
- const response = await this.fetchJson(`${this.baseUrl}/v1/crons`, {
632
- method: "GET",
633
- headers: this.apiKeyHeaders()
634
- });
635
- if (!response.ok)
636
- throw new Error(`List cron jobs failed: ${response.status} ${await cronErrorDetails(response)}`);
637
- const payload = await response.json();
638
- return payload.crons;
639
- }
640
- async getCron(cronId) {
641
- const response = await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(cronId)}`, {
642
- method: "GET",
643
- headers: this.apiKeyHeaders()
644
- });
645
- if (response.status === 404)
646
- return null;
647
- if (!response.ok)
648
- throw new Error(`Get cron job failed: ${response.status} ${await cronErrorDetails(response)}`);
649
- return await response.json();
650
- }
651
- async listCronRuns(cronId, options = {}) {
652
- const params = new URLSearchParams;
653
- if (options.limit !== undefined)
654
- params.set("limit", String(options.limit));
655
- const suffix = params.size > 0 ? `?${params}` : "";
656
- const response = await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(cronId)}/runs${suffix}`, {
657
- method: "GET",
658
- headers: this.apiKeyHeaders()
659
- });
660
- if (!response.ok)
661
- throw new Error(`List cron job runs failed: ${response.status} ${await cronErrorDetails(response)}`);
662
- const payload = await response.json();
663
- return payload.runs;
664
- }
665
- async updateCron(cronId, patch) {
666
- const response = await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(cronId)}`, {
667
- method: "PATCH",
668
- headers: this.apiKeyHeaders(),
669
- body: JSON.stringify(patch)
670
- });
671
- if (!response.ok)
672
- throw new Error(`Update cron job failed: ${response.status} ${await cronErrorDetails(response)}`);
673
- return await response.json();
674
- }
675
- async deleteCron(cronId) {
676
- const response = await this.fetchJson(`${this.baseUrl}/v1/crons/${encodeURIComponent(cronId)}`, {
677
- method: "DELETE",
678
- headers: this.apiKeyHeaders()
679
- });
680
- if (response.status === 404)
681
- return false;
682
- if (!response.ok)
683
- throw new Error(`Delete cron job failed: ${response.status} ${await cronErrorDetails(response)}`);
684
- const payload = await response.json();
685
- return payload.deleted;
686
- }
687
- scopedUrl(ref, suffix = "") {
688
- if (ref.projectSlug && ref.environmentSlug && ref.endpointId) {
689
- return `${this.baseUrl}/v1/${encodeURIComponent(ref.projectSlug)}` + `/agents/${encodeURIComponent(ref.environmentSlug)}/${encodeURIComponent(ref.endpointId)}${suffix}`;
690
- }
691
- return `${this.baseUrl}${suffix}`;
692
- }
693
- async openStream(body, targetUrl) {
694
- if (this.apiKey) {
695
- return await this.fetchCore(body, targetUrl, {
696
- Authorization: `Bearer ${this.apiKey}`
697
- });
698
- }
699
- throw new 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.");
700
- }
701
- async fetchCore(body, targetUrl, authHeaders) {
702
- try {
703
- return await this.fetchImpl(targetUrl, {
704
- method: "POST",
705
- headers: {
706
- "Content-Type": "application/json",
707
- Accept: "text/event-stream",
708
- ...authHeaders
709
- },
710
- body: JSON.stringify(body)
711
- });
712
- } catch (error) {
713
- throw new Error(`Cannot access the broods core service at ${targetUrl}. ` + `The SDK uses ${DEFAULT_CORE_BASE_URL} 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: ${error instanceof Error ? error.message : String(error)}`);
714
- }
715
- }
716
- apiKeyHeaders() {
717
- if (!this.apiKey) {
718
- throw new Error("BroodsClient requires apiKey or BROODS_API_KEY.");
719
- }
720
- return {
721
- "Content-Type": "application/json",
722
- Authorization: `Bearer ${this.apiKey}`
723
- };
724
- }
725
- async fetchJson(targetUrl, init) {
726
- try {
727
- return await this.fetchImpl(targetUrl, {
728
- ...init,
729
- headers: {
730
- "Content-Type": "application/json",
731
- ...init.headers ?? {}
732
- }
733
- });
734
- } catch (error) {
735
- throw new Error(`Cannot access the broods core service at ${targetUrl}. ` + `Cause: ${error instanceof Error ? error.message : String(error)}`);
736
- }
737
- }
738
- resolveStatusUrl(status, options) {
739
- if (typeof status !== "string")
740
- return status.statusUrl;
741
- if (/^https?:\/\//.test(status))
742
- return status;
743
- if (!options.agentId)
744
- throw new Error("Polling by status id requires agentId.");
745
- return statusUrlFor(this.baseUrl, status, options.agentId);
746
- }
747
- }
748
- function normalizeHttpServiceUrl(value) {
749
- const trimmed = value.trim();
750
- const withProtocol = /^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(trimmed) ? trimmed : `https://${trimmed}`;
751
- return stripTrailingSlash(withProtocol);
752
- }
753
- function directRunBody(input, prefix) {
754
- const eventId = input.eventId ?? `${prefix}-${Date.now()}`;
755
- return {
756
- agentId: input.agentId,
757
- eventId,
758
- conversationKey: input.conversationKey ?? eventId,
759
- events: resolveRunEvents(input),
760
- ...input.mode !== undefined ? { mode: input.mode } : {},
761
- ...input.idempotencyKey !== undefined ? { idempotencyKey: input.idempotencyKey } : {},
762
- ...input.system !== undefined ? { system: input.system } : {},
763
- ...input.model !== undefined ? { model: input.model } : {}
764
- };
765
- }
766
- function normalizeAsyncAccepted(payload, requestBody) {
767
- if (!payload || typeof payload !== "object") {
768
- throw new Error("Async response must be an object");
769
- }
770
- const statusUrl = payload.statusUrl;
771
- if (typeof statusUrl !== "string" || statusUrl.length === 0) {
772
- throw new Error("Async response missing statusUrl");
773
- }
774
- const status = parseStatusUrl(statusUrl);
775
- if (!status.statusId)
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;
780
- return {
781
- statusUrl,
782
- statusId: status.statusId,
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 } : {}
787
- };
788
- }
789
- function parseStatusUrl(statusUrl) {
790
- const url = new URL(statusUrl);
791
- const match = url.pathname.match(/\/status\/([^/]+)$/);
792
- return {
793
- statusId: match?.[1] ? decodeURIComponent(match[1]) : undefined,
794
- agentId: url.searchParams.get("agentId") ?? undefined
795
- };
796
- }
797
- function statusUrlFor(baseUrl, statusId, agentId) {
798
- return `${normalizeHttpServiceUrl(baseUrl)}/status/${encodeURIComponent(statusId)}?agentId=${encodeURIComponent(agentId)}`;
799
- }
800
- async function responseErrorDetails(response, expected) {
801
- const contentType = response.headers.get("content-type") ?? "";
802
- if (contentType.toLowerCase().includes("text/event-stream")) {
803
- await response.body?.cancel().catch(() => {});
804
- return `expected ${expected}, but the server returned an SSE stream. This usually means the core deployment routed /async to the direct streaming runner instead of the async handler.`;
805
- }
806
- const text = await response.text();
807
- if (text.length <= 2000)
808
- return text;
809
- return `${text.slice(0, 2000)}... [truncated ${text.length - 2000} chars]`;
810
- }
811
- async function cronErrorDetails(response) {
812
- const text = await response.text();
813
- if (text.includes("Request body must include eventId and conversationKey")) {
814
- return `${text}. Cron job APIs must be served by the configured baseUrl. ` + "Prefer defining stable cron jobs with defineCron(...) in broods/ and syncing with `broods dev` or `broods deploy`.";
815
- }
816
- return text;
817
- }
818
- function resolveCronInput(input) {
819
- if ("agentId" in input)
820
- return input;
821
- const agent = input.agent;
822
- const agentId = typeof agent === "string" ? agent : agent.id;
823
- const { agent: _agent, ...rest } = input;
824
- return { ...rest, agentId };
825
- }
826
- function sleep(ms, signal) {
827
- return new Promise((resolvePromise, reject) => {
828
- if (signal?.aborted) {
829
- reject(new Error("Async status polling aborted."));
830
- return;
831
- }
832
- const timer = setTimeout(resolvePromise, ms);
833
- signal?.addEventListener("abort", () => {
834
- clearTimeout(timer);
835
- reject(new Error("Async status polling aborted."));
836
- }, { once: true });
837
- });
838
- }
839
- function formatStreamError(error) {
840
- if (typeof error === "string")
841
- return error;
842
- if (!error || typeof error !== "object")
843
- return String(error);
844
- const err = error;
845
- const detail = err.data?.error?.message ?? err.message ?? err.responseBody ?? JSON.stringify(error);
846
- const prefix = err.name ? `${err.name}: ` : "";
847
- const status = err.statusCode ? ` (HTTP ${err.statusCode})` : "";
848
- return `${prefix}${detail}${status}`;
849
- }
850
- // src/websocket.ts
851
- var DEFAULT_CONNECT_TIMEOUT_MS = 2000;
852
- var WS_CONNECTING = 0;
853
- var WS_OPEN = 1;
854
-
855
- class BroodsWebSocketClient {
856
- baseUrl;
857
- apiKey;
858
- WebSocketImpl;
859
- connectTimeoutMs;
860
- constructor(options = {}) {
861
- loadBroodsRuntimeConfig();
862
- this.baseUrl = normalizeWebSocketServiceUrl(options.baseUrl || options.host || process.env.BROODS_BASE_URL || process.env.BROODS_HOST || DEFAULT_CORE_BASE_URL);
863
- this.apiKey = options.apiKey || process.env.BROODS_API_KEY || "";
864
- if (!this.apiKey)
865
- throw new Error("BroodsWebSocketClient requires apiKey or BROODS_API_KEY.");
866
- this.WebSocketImpl = options.WebSocket;
867
- this.connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
868
- }
869
- subscribe(input, handlers = {}) {
870
- const WebSocketImpl = this.resolveWebSocket();
871
- const url = this.buildUrl(input);
872
- const agentId = resolveAgentId(input);
873
- const accessError = webSocketAccessError(this.baseUrl);
874
- const socket = new WebSocketImpl(url);
875
- let opened = false;
876
- let closed = false;
877
- const timeout = setTimeout(() => {
878
- if (!opened)
879
- fail(accessError);
880
- }, this.connectTimeoutMs);
881
- const close = (code = 1000, reason = "client closed") => {
882
- if (closed)
883
- return;
884
- closed = true;
885
- clearTimeout(timeout);
886
- input.signal?.removeEventListener("abort", onAbort);
887
- if (socket.readyState === WS_OPEN || socket.readyState === WS_CONNECTING) {
888
- socket.close(code, reason);
889
- }
890
- };
891
- const fail = (error) => {
892
- handlers.onError?.(error);
893
- close(1011, "websocket error");
894
- };
895
- const finish = () => {
896
- handlers.onDone?.();
897
- close(1000, "done");
898
- };
899
- const onAbort = () => {
900
- if (socket.readyState === WS_OPEN) {
901
- socket.send(JSON.stringify({ type: "cancel" }));
902
- }
903
- fail(new Error("WebSocket run aborted."));
904
- };
905
- input.signal?.addEventListener("abort", onAbort, { once: true });
906
- socket.onopen = () => {
907
- opened = true;
908
- clearTimeout(timeout);
909
- if (input.signal?.aborted) {
910
- onAbort();
911
- return;
912
- }
913
- socket.send(JSON.stringify({
914
- type: "execute",
915
- agentId,
916
- events: resolveRunEvents(input),
917
- sessionId: input.sessionId,
918
- ...input.eventId !== undefined ? { eventId: input.eventId } : {},
919
- ...input.mode !== undefined ? { mode: input.mode } : {},
920
- ...input.idempotencyKey !== undefined ? { idempotencyKey: input.idempotencyKey } : {},
921
- ...input.system !== undefined ? { system: input.system } : {},
922
- ...input.model !== undefined ? { model: input.model } : {}
923
- }));
924
- };
925
- socket.onmessage = (event) => {
926
- if (typeof event.data !== "string")
927
- return;
928
- const payload = parseServerMessage(event.data);
929
- if (!payload)
930
- return;
931
- const effective = unwrapServerMessage(payload, handlers);
932
- handlers.onMessage?.(effective);
933
- switch (effective.type) {
934
- case "meta":
935
- handlers.onMeta?.(effective);
936
- break;
937
- case "done":
938
- finish();
939
- break;
940
- case "error":
941
- fail(new Error(formatWireError(effective.error)));
942
- break;
943
- }
944
- };
945
- socket.onerror = () => {
946
- fail(accessError);
947
- };
948
- socket.onclose = (event) => {
949
- clearTimeout(timeout);
950
- input.signal?.removeEventListener("abort", onAbort);
951
- if (closed || event.code === 1000)
952
- return;
953
- fail(new Error(event.reason || "WebSocket connection closed."));
954
- };
955
- return {
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
- },
1008
- close
1009
- };
1010
- }
1011
- async* stream(input) {
1012
- const queue = [];
1013
- let done = false;
1014
- let error = null;
1015
- let wake = null;
1016
- const wakeReader = () => {
1017
- wake?.();
1018
- wake = null;
1019
- };
1020
- const subscription = this.subscribe(input, {
1021
- onMessage(message) {
1022
- queue.push(message);
1023
- wakeReader();
1024
- },
1025
- onDone() {
1026
- done = true;
1027
- wakeReader();
1028
- },
1029
- onError(nextError) {
1030
- error = nextError;
1031
- wakeReader();
1032
- }
1033
- });
1034
- try {
1035
- while (true) {
1036
- if (queue.length > 0) {
1037
- yield queue.shift();
1038
- continue;
1039
- }
1040
- if (error)
1041
- throw error;
1042
- if (done)
1043
- return;
1044
- await new Promise((resolve2) => {
1045
- wake = resolve2;
1046
- });
1047
- }
1048
- } finally {
1049
- subscription.close();
1050
- }
1051
- }
1052
- buildUrl(input) {
1053
- const endpointId = resolveEndpointId(input);
1054
- const projectSlug = input.projectSlug ?? input.agent?.projectSlug;
1055
- const environmentSlug = input.environmentSlug ?? input.agent?.environmentSlug;
1056
- const projectPrefix = projectSlug ? `/${projectSlug}` : "";
1057
- const environmentPrefix = environmentSlug ? `/${environmentSlug}` : "";
1058
- const wsBaseUrl = toWebSocketBaseUrl(this.baseUrl);
1059
- return `${wsBaseUrl}/v1${projectPrefix}/agents${environmentPrefix}/${encodeURIComponent(endpointId)}/ws` + `?token=${encodeURIComponent(this.apiKey)}`;
1060
- }
1061
- resolveWebSocket() {
1062
- const WebSocketImpl = this.WebSocketImpl ?? globalThis.WebSocket;
1063
- if (!WebSocketImpl) {
1064
- throw new Error("WebSocket is not available. Pass a WebSocket implementation in BroodsWebSocketClient options.");
1065
- }
1066
- return WebSocketImpl;
1067
- }
1068
- }
1069
- function resolveAgentId(input) {
1070
- const agentId = input.agentId ?? input.agent?.id ?? input.endpointId;
1071
- if (!agentId) {
1072
- throw new Error("WebSocket run requires agent, agentId, or endpointId.");
1073
- }
1074
- return agentId;
1075
- }
1076
- function resolveEndpointId(input) {
1077
- const endpointId = input.endpointId ?? input.agent?.endpointId ?? input.agent?.id;
1078
- if (!endpointId) {
1079
- throw new Error("WebSocket run requires agent or endpointId.");
1080
- }
1081
- return endpointId;
1082
- }
1083
- function toWebSocketBaseUrl(url) {
1084
- const parsed = new URL(normalizeWebSocketServiceUrl(url));
1085
- if (parsed.protocol === "https:")
1086
- parsed.protocol = "wss:";
1087
- if (parsed.protocol === "http:")
1088
- parsed.protocol = "ws:";
1089
- return stripTrailingSlash(parsed.toString());
1090
- }
1091
- function normalizeWebSocketServiceUrl(value) {
1092
- const trimmed = value.trim();
1093
- if (/^wss?:\/\//.test(trimmed))
1094
- return stripTrailingSlash(trimmed);
1095
- return normalizeHttpServiceUrl(trimmed);
1096
- }
1097
- function webSocketAccessError(baseUrl) {
1098
- return new Error(`Cannot access the WebSocket service at ${baseUrl}.`);
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
- }
1107
- function parseServerMessage(data) {
1108
- try {
1109
- const value = JSON.parse(data);
1110
- return typeof value === "object" && value !== null && typeof value.type === "string" ? value : null;
1111
- } catch {
1112
- return null;
1113
- }
1114
- }
1115
- function formatWireError(error) {
1116
- if (typeof error === "string")
1117
- return error;
1118
- if (error && typeof error === "object" && typeof error.message === "string") {
1119
- return error.message;
1120
- }
1121
- return error === undefined ? "WebSocket stream error." : JSON.stringify(error);
1122
- }
1123
- // src/resources.ts
1124
- var RESOURCE_MARKER = Symbol.for("broods.resource");
1125
- var CONFIG_MARKER = Symbol.for("broods.config");
1126
- var CHANNEL_MARKER = Symbol.for("broods.channel");
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
- }
1132
- return { __beeblastEnv: true, name };
1133
- }, {
1134
- get(target, property, receiver) {
1135
- if (typeof property === "string" && ENV_NAME_PATTERN.test(property)) {
1136
- throw new Error(`env.${property} is not supported; use env("${property}")`);
1137
- }
1138
- return Reflect.get(target, property, receiver);
1139
- }
1140
- });
1141
- function defineResource(kind, name, description, config) {
1142
- return {
1143
- [RESOURCE_MARKER]: true,
1144
- kind,
1145
- name,
1146
- ...description ? { description } : {},
1147
- config
1148
- };
1149
- }
1150
- function defineChannel(type, config) {
1151
- const { workspaceScope, ...channelConfig } = config;
1152
- return {
1153
- [CHANNEL_MARKER]: true,
1154
- kind: "channel",
1155
- type,
1156
- ...workspaceScope ? { workspaceScope } : {},
1157
- config: {
1158
- ...channelConfig,
1159
- ...workspaceScope ? { workspaceScope } : {}
1160
- }
1161
- };
1162
- }
1163
- function defineTelegramChannel(config) {
1164
- return defineChannel("telegram", config);
1165
- }
1166
- function defineGitHubChannel(config) {
1167
- return defineChannel("github", config);
1168
- }
1169
- function defineSlackChannel(config) {
1170
- return defineChannel("slack", config);
1171
- }
1172
- function defineDiscordChannel(config) {
1173
- return defineChannel("discord", config);
1174
- }
1175
- function definePancakeChannel(config) {
1176
- return defineChannel("pancake", config);
1177
- }
1178
- function defineZaloChannel(config) {
1179
- return defineChannel("zalo", config);
1180
- }
1181
- function defineBroods(config) {
1182
- return { [CONFIG_MARKER]: true, config };
1183
- }
1184
- function defineAgent(input) {
1185
- const { name, description, ...config } = input;
1186
- return defineResource("agent", name, description, config);
1187
- }
1188
- function defineWorkspace(input) {
1189
- const { name, description, ...config } = input;
1190
- return defineResource("workspace", name, description, config);
1191
- }
1192
- function defineSandbox(input) {
1193
- const { name, description, ...config } = input;
1194
- return defineResource("sandbox", name, description, config);
1195
- }
1196
- function defineSkill(input) {
1197
- const { name, description, ...config } = input;
1198
- return defineResource("skill", name, description, config);
1199
- }
1200
- function defineTool(input) {
1201
- const { name, ...config } = input;
1202
- return defineResource("tool", name, undefined, config);
1203
- }
1204
- function definePolicy(input) {
1205
- const { name, description, ...config } = input;
1206
- return defineResource("policy", name, description, config);
1207
- }
1208
- function defineCron(input) {
1209
- const { name, description, ...config } = input;
1210
- return defineResource("cron", name, description, config);
1211
- }
1212
- function isResource(value) {
1213
- return Boolean(value && typeof value === "object" && value[RESOURCE_MARKER]);
1214
- }
1215
- function isChannelDefinition(value) {
1216
- return Boolean(value && typeof value === "object" && value[CHANNEL_MARKER]);
1217
- }
1218
- function isBroodsConfig(value) {
1219
- return Boolean(value && typeof value === "object" && value[CONFIG_MARKER]);
1220
- }
1221
- export {
1222
- toWebSocketBaseUrl,
1223
- resolveRunEvents,
1224
- readSseStream,
1225
- normalizeHttpServiceUrl,
1226
- isResource,
1227
- isObservabilityClientMessage,
1228
- isChannelDefinition,
1229
- isBroodsConfig,
1230
- envPlaceholder,
1231
- env,
1232
- defineZaloChannel,
1233
- defineWorkspace,
1234
- defineTool,
1235
- defineTelegramChannel,
1236
- defineSlackChannel,
1237
- defineSkill,
1238
- defineSandbox,
1239
- definePolicy,
1240
- definePancakeChannel,
1241
- defineGitHubChannel,
1242
- defineDiscordChannel,
1243
- defineCron,
1244
- defineBroods,
1245
- defineAgent,
1246
- BroodsWebSocketClient as WebsocketClient,
1247
- BroodsWebSocketClient as WebSocketClient,
1248
- MAX_OBSERVABILITY_BACKFILL,
1249
- IngressAcceptedError,
1250
- DEFAULT_CORE_BASE_URL,
1251
- BroodsWebSocketClient,
1252
- BroodsClient,
1253
- BroodsAccountClient,
1254
- BroodsAccountApiError
1255
- };
1
+ import{a as M,b as h,c as O}from"./chunk-9qtppg6j.js";import{A as _,B as P,C as H,D as U,E as B,F as K,G as j,H as F,I as G,J as z,K as Z,d as x,e as I,n as v,o as k,p as S,q as A,r as R,s as W,t as L,u as w,v as E,w as D,x as T,y as q,z as N}from"./chunk-18hxt2an.js";import"./chunk-xv4t40dh.js";var o=500;function a(s){if(typeof s!=="object"||s===null)return!1;let e=s;if(e.type==="subscribe"){let t=e.stream;if(t!=="logs"&&t!=="traces")return!1;let r=e.backfill;if(r!==void 0&&(typeof r!=="number"||!Number.isSafeInteger(r)||r<0||r>500))return!1;if(e.liveOnly!==void 0&&typeof e.liveOnly!=="boolean")return!1;let n=e.minLevel;if(n!==void 0&&n!=="INFO"&&n!=="WARN"&&n!=="ERROR")return!1;return!0}if(e.type==="unsubscribe"){let t=e.stream;return t==="logs"||t==="traces"}return!1}export{W as toWebSocketBaseUrl,x as resolveRunEvents,I as readSseStream,A as normalizeHttpServiceUrl,G as isResource,a as isObservabilityClientMessage,z as isChannelDefinition,Z as isBroodsConfig,M as envPlaceholder,L as env,N as defineZaloChannel,H as defineWorkspace,K as defineTool,w as defineTelegramChannel,D as defineSlackChannel,B as defineSkill,U as defineSandbox,j as definePolicy,q as definePancakeChannel,E as defineGitHubChannel,T as defineDiscordChannel,F as defineCron,_ as defineBroods,P as defineAgent,R as WebsocketClient,R as WebSocketClient,o as MAX_OBSERVABILITY_BACKFILL,k as IngressAcceptedError,v as DEFAULT_CORE_BASE_URL,R as BroodsWebSocketClient,S as BroodsClient,O as BroodsAccountClient,h as BroodsAccountApiError};