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