broods 0.48.0 → 0.49.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 +109 -49
- package/dist/chunk-084dph9c.js +4 -0
- package/dist/{chunk-fb10617g.js → chunk-bbgvyp29.js} +1 -1
- package/dist/cli/index.js +4 -4
- package/dist/index.d.ts +109 -49
- package/package.json +1 -1
- package/dist/chunk-11g7dgvw.js +0 -4
package/dist/account.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { SystemModelMessage, LanguageModelCallOptions, RequestOptions, streamText, JSONSchema7, ModelMessage } from 'ai';
|
|
2
|
+
import * as convex_values from 'convex/values';
|
|
3
|
+
import { Infer } from 'convex/values';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Account-owned code hook upload validation for Convex config-plane sync.
|
|
@@ -777,7 +779,6 @@ interface SandboxConfig {
|
|
|
777
779
|
onCreate?: string[];
|
|
778
780
|
onResume?: string[];
|
|
779
781
|
timeout?: number;
|
|
780
|
-
memoryLimit?: number;
|
|
781
782
|
outputLimitBytes?: number;
|
|
782
783
|
envVars?: Record<string, undefined | string>;
|
|
783
784
|
options?: Record<string, unknown>;
|
|
@@ -844,55 +845,114 @@ interface WorkspaceConfig {
|
|
|
844
845
|
}
|
|
845
846
|
|
|
846
847
|
/**
|
|
847
|
-
*
|
|
848
|
-
*
|
|
849
|
-
*
|
|
848
|
+
* Versioned policy document accepted by account-management CRUD, as the
|
|
849
|
+
* `agentPolicies` and `accountRoles` tables store it. `mode` says how hard
|
|
850
|
+
* the policy bites where it is attached; omitted reads as `audit`.
|
|
850
851
|
*/
|
|
851
|
-
declare const
|
|
852
|
-
|
|
853
|
-
* API action namespace for account roles: one read/write pair per config-plane
|
|
854
|
-
* resource route. Roles carry the same PolicyDocument shape as agent policies;
|
|
855
|
-
* each caller passes its action set to `normalizePolicyDocument`.
|
|
856
|
-
*/
|
|
857
|
-
declare const API_POLICY_ACTIONS: readonly ["account:read", "account:write", "agents:read", "agents:write", "audit:read", "audit:write", "channels:read", "channels:write", "crons:read", "crons:write", "env:read", "env:write", "hooks:read", "hooks:write", "mcp:read", "mcp:write", "policies:read", "policies:write", "sandboxes:read", "sandboxes:write", "skills:read", "skills:write", "workspaces:read", "workspaces:write"];
|
|
858
|
-
type AgentPolicyAction = (typeof AGENT_POLICY_ACTIONS)[number];
|
|
859
|
-
type ApiPolicyAction = (typeof API_POLICY_ACTIONS)[number];
|
|
860
|
-
type PolicyAction = AgentPolicyAction | ApiPolicyAction;
|
|
861
|
-
interface PolicyCondition {
|
|
862
|
-
attribute: string;
|
|
863
|
-
operator: PolicyConditionOperator;
|
|
864
|
-
value: string | number | boolean | string[] | number[] | boolean[];
|
|
865
|
-
}
|
|
866
|
-
type PolicyConditionOperator = "equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains";
|
|
867
|
-
/**
|
|
868
|
-
* Versioned policy document accepted by account-management CRUD.
|
|
869
|
-
*/
|
|
870
|
-
interface PolicyDocument {
|
|
852
|
+
declare const policyDocumentValidator: convex_values.VObject<{
|
|
853
|
+
mode?: "enforce" | "audit" | undefined;
|
|
871
854
|
version: 1;
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
855
|
+
rules: {
|
|
856
|
+
resources?: {
|
|
857
|
+
toolNames?: string[] | undefined;
|
|
858
|
+
mcpIds?: string[] | undefined;
|
|
859
|
+
workspaceIds?: string[] | undefined;
|
|
860
|
+
workspaceNames?: string[] | undefined;
|
|
861
|
+
filePaths?: string[] | undefined;
|
|
862
|
+
subagentIds?: string[] | undefined;
|
|
863
|
+
skillPaths?: string[] | undefined;
|
|
864
|
+
resourceIds?: string[] | undefined;
|
|
865
|
+
} | undefined;
|
|
866
|
+
conditions?: {
|
|
867
|
+
attribute: string;
|
|
868
|
+
operator: "equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains";
|
|
869
|
+
value: string | number | boolean | string[] | number[] | boolean[];
|
|
870
|
+
}[] | undefined;
|
|
871
|
+
id: string;
|
|
872
|
+
effect: "allow" | "deny";
|
|
873
|
+
actions: string[];
|
|
874
|
+
}[];
|
|
875
|
+
}, {
|
|
876
|
+
version: convex_values.VLiteral<1, "required">;
|
|
877
|
+
mode: convex_values.VUnion<"enforce" | "audit" | undefined, [convex_values.VLiteral<"enforce", "required">, convex_values.VLiteral<"audit", "required">], "optional", never>;
|
|
878
|
+
rules: convex_values.VArray<{
|
|
879
|
+
resources?: {
|
|
880
|
+
toolNames?: string[] | undefined;
|
|
881
|
+
mcpIds?: string[] | undefined;
|
|
882
|
+
workspaceIds?: string[] | undefined;
|
|
883
|
+
workspaceNames?: string[] | undefined;
|
|
884
|
+
filePaths?: string[] | undefined;
|
|
885
|
+
subagentIds?: string[] | undefined;
|
|
886
|
+
skillPaths?: string[] | undefined;
|
|
887
|
+
resourceIds?: string[] | undefined;
|
|
888
|
+
} | undefined;
|
|
889
|
+
conditions?: {
|
|
890
|
+
attribute: string;
|
|
891
|
+
operator: "equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains";
|
|
892
|
+
value: string | number | boolean | string[] | number[] | boolean[];
|
|
893
|
+
}[] | undefined;
|
|
894
|
+
id: string;
|
|
895
|
+
effect: "allow" | "deny";
|
|
896
|
+
actions: string[];
|
|
897
|
+
}[], convex_values.VObject<{
|
|
898
|
+
resources?: {
|
|
899
|
+
toolNames?: string[] | undefined;
|
|
900
|
+
mcpIds?: string[] | undefined;
|
|
901
|
+
workspaceIds?: string[] | undefined;
|
|
902
|
+
workspaceNames?: string[] | undefined;
|
|
903
|
+
filePaths?: string[] | undefined;
|
|
904
|
+
subagentIds?: string[] | undefined;
|
|
905
|
+
skillPaths?: string[] | undefined;
|
|
906
|
+
resourceIds?: string[] | undefined;
|
|
907
|
+
} | undefined;
|
|
908
|
+
conditions?: {
|
|
909
|
+
attribute: string;
|
|
910
|
+
operator: "equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains";
|
|
911
|
+
value: string | number | boolean | string[] | number[] | boolean[];
|
|
912
|
+
}[] | undefined;
|
|
913
|
+
id: string;
|
|
914
|
+
effect: "allow" | "deny";
|
|
915
|
+
actions: string[];
|
|
916
|
+
}, {
|
|
917
|
+
id: convex_values.VString<string, "required">;
|
|
918
|
+
effect: convex_values.VUnion<"allow" | "deny", [convex_values.VLiteral<"allow", "required">, convex_values.VLiteral<"deny", "required">], "required", never>;
|
|
919
|
+
actions: convex_values.VArray<string[], convex_values.VString<string, "required">, "required">;
|
|
920
|
+
resources: convex_values.VObject<{
|
|
921
|
+
toolNames?: string[] | undefined;
|
|
922
|
+
mcpIds?: string[] | undefined;
|
|
923
|
+
workspaceIds?: string[] | undefined;
|
|
924
|
+
workspaceNames?: string[] | undefined;
|
|
925
|
+
filePaths?: string[] | undefined;
|
|
926
|
+
subagentIds?: string[] | undefined;
|
|
927
|
+
skillPaths?: string[] | undefined;
|
|
928
|
+
resourceIds?: string[] | undefined;
|
|
929
|
+
} | undefined, {
|
|
930
|
+
toolNames: convex_values.VArray<string[] | undefined, convex_values.VString<string, "required">, "optional">;
|
|
931
|
+
/** MCP registration ids, for scoping tool.call rules per server (#331). */
|
|
932
|
+
mcpIds: convex_values.VArray<string[] | undefined, convex_values.VString<string, "required">, "optional">;
|
|
933
|
+
workspaceIds: convex_values.VArray<string[] | undefined, convex_values.VString<string, "required">, "optional">;
|
|
934
|
+
workspaceNames: convex_values.VArray<string[] | undefined, convex_values.VString<string, "required">, "optional">;
|
|
935
|
+
filePaths: convex_values.VArray<string[] | undefined, convex_values.VString<string, "required">, "optional">;
|
|
936
|
+
subagentIds: convex_values.VArray<string[] | undefined, convex_values.VString<string, "required">, "optional">;
|
|
937
|
+
skillPaths: convex_values.VArray<string[] | undefined, convex_values.VString<string, "required">, "optional">;
|
|
938
|
+
resourceIds: convex_values.VArray<string[] | undefined, convex_values.VString<string, "required">, "optional">;
|
|
939
|
+
}, "optional", "toolNames" | "mcpIds" | "workspaceIds" | "workspaceNames" | "filePaths" | "subagentIds" | "skillPaths" | "resourceIds">;
|
|
940
|
+
conditions: convex_values.VArray<{
|
|
941
|
+
attribute: string;
|
|
942
|
+
operator: "equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains";
|
|
943
|
+
value: string | number | boolean | string[] | number[] | boolean[];
|
|
944
|
+
}[] | undefined, convex_values.VObject<{
|
|
945
|
+
attribute: string;
|
|
946
|
+
operator: "equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains";
|
|
947
|
+
value: string | number | boolean | string[] | number[] | boolean[];
|
|
948
|
+
}, {
|
|
949
|
+
attribute: convex_values.VString<string, "required">;
|
|
950
|
+
operator: convex_values.VUnion<"equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains", convex_values.VLiteral<"equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains", "required">[], "required", never>;
|
|
951
|
+
value: convex_values.VUnion<string | number | boolean | string[] | number[] | boolean[], [convex_values.VString<string, "required">, convex_values.VFloat64<number, "required">, convex_values.VBoolean<boolean, "required">, convex_values.VArray<string[], convex_values.VString<string, "required">, "required">, convex_values.VArray<number[], convex_values.VFloat64<number, "required">, "required">, convex_values.VArray<boolean[], convex_values.VBoolean<boolean, "required">, "required">], "required", never>;
|
|
952
|
+
}, "required", "attribute" | "operator" | "value">, "optional">;
|
|
953
|
+
}, "required", "id" | "effect" | "actions" | "resources" | "conditions" | "resources.toolNames" | "resources.mcpIds" | "resources.workspaceIds" | "resources.workspaceNames" | "resources.filePaths" | "resources.subagentIds" | "resources.skillPaths" | "resources.resourceIds">, "required">;
|
|
954
|
+
}, "required", "version" | "mode" | "rules">;
|
|
955
|
+
type PolicyDocument = Infer<typeof policyDocumentValidator>;
|
|
896
956
|
|
|
897
957
|
/**
|
|
898
958
|
* Channel record validation for the config plane, the single home of the
|
|
@@ -1553,7 +1613,7 @@ declare class BroodsAccountClient {
|
|
|
1553
1613
|
resumeSandbox(sandboxId: string, reservationKey: string): Promise<SandboxLifecycleResult>;
|
|
1554
1614
|
/** Terminate a persistent sandbox reservation and drop its live-instance row. Throws on 404/403/409. */
|
|
1555
1615
|
terminateSandbox(sandboxId: string, reservationKey: string): Promise<SandboxLifecycleResult>;
|
|
1556
|
-
/** Snapshot a persistent sandbox reservation into
|
|
1616
|
+
/** Snapshot a persistent sandbox reservation into an image any sandbox of that provider can pin with `snapshot` (`sandbox`, `daytona`, `e2b`, `vercel`, or `lambda`, which answers `building` until its image build lands). Throws on 404/403/409. */
|
|
1557
1617
|
snapshotSandbox(sandboxId: string, reservationKey: string, name: string): Promise<SandboxSnapshotResult>;
|
|
1558
1618
|
/** Mint a short-lived sealed ticket for an interactive PTY session on a persistent sandbox (`sandbox`/`lambda`/`cloudflare` providers). Throws on 404/403/409. */
|
|
1559
1619
|
openSandboxTerminal(sandboxId: string, reservationKey: string): Promise<SandboxTerminalTicket>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{U,z}from"./chunk-hb3c3xt9.js";import{e,o,a,l,n,t,d,C,s,p,r}from"./chunk-n890pdvc.js";import{se,fe,D}from"./cli/index.js";import{spawn as te}from"node:child_process";import{statfsSync as oe}from"node:fs";import{availableParallelism as re,homedir as ne,hostname as ie,totalmem as ae}from"node:os";import{performance as L}from"node:perf_hooks";var H=["cursor_position","double_click","hold_key","key","left_click","left_click_drag","left_mouse_down","left_mouse_up","middle_click","mouse_move","right_click","screenshot","scroll","triple_click","type","wait","zoom"];var k={badFrame:{code:4400,reason:"Malformed frame"},occupied:{code:4423,reason:"Already connected from another daemon"},replaced:{code:4409,reason:"Replaced by a newer connection"},unauthorized:{code:4401,reason:"Unauthorized; run broods login"},unknownSandbox:{code:4404,reason:"No machine sandbox with that name in this account"}};var N="/v1/machines/ws",P=t({action:p(H),coordinate:n(o().int()).length(2).optional().describe("[x, y] in screenshot pixels for mouse actions."),start_coordinate:n(o().int()).length(2).optional().describe("[x, y] where a left_click_drag starts."),text:e().optional().describe("Text for type, the key chord for key and hold_key, or modifiers to hold during a click, drag or scroll."),scroll_direction:p(["down","left","right","up"]).optional(),scroll_amount:o().int().min(1).optional().describe("Scroll clicks, default 3."),duration:o().min(0).max(300).optional().describe("Seconds for wait and hold_key."),region:n(o().int()).length(4).optional().describe("[x0, y0, x1, y1] of the screenshot to zoom into."),repeat:o().int().min(1).max(100).optional().describe("How many times to press the key.")}),B=P.extend({type:r("computer"),id:e()}),G=t({type:r("computer-result"),id:e(),text:e().optional(),image:t({data:e(),mediaType:e()}).optional(),app:e().optional(),error:e().optional()}),W=t({type:r("exec"),id:e(),code:e(),cwd:e().optional(),env:s(e(),e()).optional(),timeoutSeconds:o().positive(),outputLimitBytes:o().positive()}),J=t({type:r("hello"),sandbox:e().min(1),hostname:e().optional(),platform:e().optional(),computer:a().optional(),mcp:n(e()).optional(),instance:e().optional(),force:a().optional(),specs:t({vcpu:o().positive(),memoryMb:o().positive(),storageGb:o().positive().optional()}).optional().catch(void 0)}),K=t({type:r("mcp-call"),id:e(),server:e(),tool:e(),args:s(e(),l())}),X=t({type:r("mcp-list"),id:e(),server:e()}),Y=t({type:r("mcp-result"),id:e(),result:d({content:n(l())}).optional(),error:e().optional()}),q=t({type:r("mcp-tools"),id:e(),tools:n(d({name:e(),inputSchema:s(e(),l())})).optional(),error:e().optional()}),Z=t({type:r("ready"),sandboxId:e()}),Q=t({type:r("result"),id:e(),exitCode:o().nullable(),stdout:e(),stderr:e(),durationMs:o(),timedOut:a(),truncated:a()}),V=C("type",[B,W,K,X,Z]),Fe=C("type",[G,J,Y,q,Q]);function T(i){let c=new URL(N,i);return c.protocol=c.protocol==="https:"?"wss:":"ws:",c.toString()}function R(i){return ee(V,i)}function ee(i,c){if(typeof c!=="string")return null;try{let u=i.safeParse(JSON.parse(c));return u.success?u.data:null}catch{return null}}var ce=new Set([k.occupied.code,k.replaced.code,k.unauthorized.code,k.unknownSandbox.code]),A=72,_=30000,I=1000;function le(i,c,u){return new Promise((f)=>{let m=L.now(),y=new E(i.outputLimitBytes),w=new E(i.outputLimitBytes),b=!1,v=!1,M=(h,O)=>{if(v)return;if(v=!0,clearTimeout(x),u?.removeEventListener("abort",g),O)w.append(Buffer.from(`${O}
|
|
2
|
+
`));f({type:"result",id:i.id,exitCode:h,stdout:y.text(),stderr:w.text(),durationMs:Math.round(L.now()-m),timedOut:b,truncated:y.truncated||w.truncated})},F=te("bash",["-lc",i.code],{cwd:i.cwd??c,env:{...U(),...i.env},detached:!0,stdio:["ignore","pipe","pipe"]}),S=()=>{if(!F.pid)return;try{process.kill(-F.pid,"SIGKILL")}catch{F.kill("SIGKILL")}},x=setTimeout(()=>{b=!0,S()},i.timeoutSeconds*1000),g=()=>{S(),M(null,"stopped: the daemon closed its socket")};if(u?.addEventListener("abort",g,{once:!0}),F.stdout.on("data",(h)=>y.append(h)),F.stderr.on("data",(h)=>w.append(h)),F.on("error",(h)=>M(null,h.message)),F.on("close",(h)=>M(h)),u?.aborted)g()})}async function Oe(i){let c=fe(),u=crypto.randomUUID(),f=i.mcpFile?await he(i.mcpFile,i.log):null,m=i.computer?await ue(i.log):null,y=I;try{while(!i.signal.aborted){let w=Date.now(),b;try{b=await i.credential()}catch(M){if(M instanceof D)throw M;i.log(`stage session unavailable (${M instanceof Error?M.message:String(M)}), retrying in ${Math.round(y/1000)}s`),await se(y,i.signal),y=Math.min(y*2,_);continue}let v=await ge(i,b,u,c,m,f);if(i.signal.aborted)return;if(ce.has(v.code))throw Error(v.reason||`core closed the socket (${v.code})`);if(Date.now()-w>_)y=I;i.log(`disconnected (${v.reason||v.code}), reconnecting in ${Math.round(y/1000)}s`),await se(y,i.signal),y=Math.min(y*2,_)}}finally{m?.stop(),await f?.stop()}}class E{#o=[];#e;#t=0;truncated=!1;constructor(i){this.#e=i}append(i){if(this.#t>=this.#e){this.truncated=!0;return}let c=this.#e-this.#t;if(i.byteLength>c){this.#o.push(i.subarray(0,c)),this.#t=this.#e,this.truncated=!0;return}this.#o.push(i),this.#t+=i.byteLength}text(){let i=Buffer.concat(this.#o).toString("utf8");return this.truncated?`${i}
|
|
3
|
+
[output truncated]`:i}}function pe(i){let c=[i.action];if(i.coordinate)c.push(`at ${i.coordinate.join(",")}`);if(i.text!==void 0)c.push(JSON.stringify(j(i.text)));if(i.region)c.push(`region ${i.region.join(",")}`);return c.join(" ")}function de(){let i=me();return{vcpu:re(),memoryMb:Math.round(ae()/1048576),...i?{storageGb:i}:{}}}function me(){try{let i=oe(ne());return Math.round(i.blocks*i.bsize/1073741824)}catch{return}}function j(i){let c=i.trim().split(`
|
|
4
|
+
`)[0]??"";return c.length>A?`${c.slice(0,A)}…`:c}async function ue(i){let{startDesktop:c}=await import("./chunk-5tnnqapc.js"),{display:u,driver:f,permissions:m}=await c(!1);if(i(`computer use on, ${u.width}x${u.height} screenshots`),!m.screenRecording||!m.accessibility)i(" screen recording or accessibility is missing, run `broods machine --doctor --request`");return f}async function he(i,c){let{McpHost:u,readMcpServersFile:f}=await import("./chunk-46v60rbx.js"),m=new u(f(i),c);return c(`mcp servers from ${i}: ${m.names().join(", ")}`),m}async function ye(i,c,u){try{if(i.type==="mcp-list"){let m=await c.listTools(i.server);return u(` mcp ${i.server}: ${m.length} tools`),{type:"mcp-tools",id:i.id,tools:m}}u(` mcp ${i.server}.${i.tool}`);let f=await c.callTool(i.server,i.tool,i.args);return{type:"mcp-result",id:i.id,result:f}}catch(f){let m=f instanceof Error?f.message:String(f);return u(` mcp ${i.server}: ${m}`),{type:i.type==="mcp-call"?"mcp-result":"mcp-tools",id:i.id,error:m}}}function ge(i,c,u,f,m,y){return new Promise((w)=>{let b=new f(T(i.baseUrl),z(c)),v=new AbortController,M=()=>b.close(1000,"daemon stopped"),F=(x)=>{if(b.readyState===b.OPEN)b.send(JSON.stringify(x))},S=(x,g)=>{i.signal.removeEventListener("abort",M),v.abort(),w({code:x,reason:g})};i.signal.addEventListener("abort",M,{once:!0}),b.onopen=()=>F({type:"hello",sandbox:i.sandbox,hostname:ie(),platform:process.platform,computer:m!==null,mcp:y?.names(),instance:u,force:i.force,specs:de()}),b.onmessage=(x)=>{let g=R(x.data);if(g?.type==="ready"){i.log(`connected as ${i.sandbox} (${g.sandboxId})`);return}if(g?.type==="computer"&&m){i.log(` ${pe(g)}`),m.run(g).then((h)=>{if(h.error)i.log(` error: ${h.error}`);F(h)});return}if((g?.type==="mcp-call"||g?.type==="mcp-list")&&y){ye(g,y,i.log).then(F);return}if(g?.type!=="exec")return;i.log(`$ ${j(g.code)}`),le(g,i.cwd,v.signal).then((h)=>{i.log(` exit ${h.exitCode??"none"} in ${h.durationMs}ms${h.timedOut?" (timed out)":""}`),F(h)})},b.onerror=()=>S(1006,"connection failed"),b.onclose=(x)=>S(x.code,x.reason)})}export{Oe as runMachineDaemon};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j}from"./chunk-r86n05ec.js";import{Z,R}from"./chunk-bb3jfhv3.js";import{e,o,a,l,s}from"./chunk-n890pdvc.js";var E={project:e().optional(),stage:e().optional()},M={sandboxId:e().min(1),reservationKey:e().min(1)},v=a().describe("Must be true, after the owner has agreed to this delete."),b="'********' keeps a stored secret and null deletes a field: never send a placeholder you did not read from a get.",A=[{singular:"agent",plural:"agents",key:"agentId",createHint:"Needs name and config; a 409 returns the existing agentId.",deleteHint:"Deleting an agent also drops its runtime rows.",updateHint:b,list:(t)=>t.listAgents(),get:(t,r)=>t.getAgent(r),create:(t,r)=>t.createAgent(r),update:(t,r,n)=>t.updateAgent(r,n),remove:(t,r)=>t.deleteAgent(r)},{singular:"cron",plural:"crons",key:"cronId",createHint:"Needs name, agentId, scheduleExpression (rate/cron/at) and input or events.",list:(t)=>t.listCrons(),get:(t,r)=>t.getCron(r),create:(t,r)=>t.createCron(r),update:(t,r,n)=>t.updateCron(r,n),remove:(t,r)=>t.deleteCron(r)},{singular:"sandbox",plural:"sandboxes",key:"sandboxId",updateHint:b,list:(t)=>t.listSandboxes(),get:(t,r)=>t.getSandbox(r),create:(t,r)=>t.createSandbox(r),update:(t,r,n)=>t.updateSandbox(r,n),remove:(t,r)=>t.deleteSandbox(r)},{singular:"workspace",plural:"workspaces",key:"workspaceId",list:(t)=>t.listWorkspaces(),get:(t,r)=>t.getWorkspace(r),create:(t,r)=>t.createWorkspace(r),update:(t,r,n)=>t.updateWorkspace(r,n),remove:(t,r)=>t.deleteWorkspace(r)},{singular:"policy",plural:"policies",key:"policyId",createHint:"Agent-runtime policy document (tool.call, skill.load), not API credential scoping.",list:(t)=>t.listPolicies(),get:(t,r)=>t.getPolicy(r),create:(t,r)=>t.createPolicy(r),update:(t,r,n)=>t.updatePolicy(r,n),remove:(t,r)=>t.deletePolicy(r)},{singular:"role",plural:"roles",key:"roleId",createHint:"Needs name and a version-1 policy over the API namespace, e.g. agents:read. Account key only.",list:(t)=>t.listRoles(),get:(t,r)=>t.getRole(r),create:(t,r)=>t.createRole(r),update:(t,r,n)=>t.updateRole(r,n),remove:(t,r)=>t.deleteRole(r)},{singular:"channel",plural:"channels",key:"channelId",list:(t)=>t.listChannels(),get:(t,r)=>t.getChannel(r),create:(t,r)=>t.createChannel(r),update:(t,r,n)=>t.updateChannel(r,n),remove:(t,r)=>t.deleteChannel(r)},{singular:"skill",plural:"skills",key:"skillName",createHint:'source is "files" (base64), "json" or "github". The stored name comes from the SKILL.md frontmatter.',list:(t)=>t.listSkills(),get:(t,r)=>t.getSkill(r),create:(t,r)=>t.createSkill(r),remove:(t,r)=>t.deleteSkill(r)},{singular:"mcp",plural:"mcp",key:"serverId",scoped:!0,list:(t,r)=>t.listMcp(r),get:(t,r)=>t.getMcp(r),create:(t,r,n)=>t.createMcp(n,r),update:(t,r,n)=>t.updateMcp(r,n),remove:(t,r)=>t.deleteMcp(r)}];function Pt(t={}){let r=Z()?new R({}):null;if(!r&&!t.cli)throw Error("broods mcp needs a credential: set BROODS_SESSION_TOKEN or BROODS_ACCOUNT_SECRET, or run `broods login`.");let n=new j({name:"broods",version:"1"},{capabilities:{tools:{}}});if(r){for(let i of A)x(n,r,i,t.scope??{});N(n,r)}if(t.cli)O(n,t.cli);if(t.allowDestructive)q(n,r,t.cli??null);return n}function y(t,r){if(!t)throw Error(`Refusing without confirm:true. ${r}`)}async function d(t){try{return P(await t())}catch(r){return k(r)}}function k(t){return{content:[{type:"text",text:t instanceof Error?t.message:String(t)}],isError:!0}}function P(t){return{content:[{type:"text",text:JSON.stringify(t??null,null,2)}]}}function T(t,r,n,i){if(!t.scoped)return;let c=n??r.project,p=i??r.stage;if(!c||!p)throw Error(`${t.plural} live in one stage, so this call needs both 'project' and 'stage'`);return{project:c,stage:p}}function O(t,r){t.registerTool("list-orgs",{description:"Every organization this login can reach, with the current one marked.",inputSchema:{}},async()=>await d(async()=>{let n=await r.getOnboarding();return{currentOrgId:n.currentOrgId,orgs:n.orgs}})),t.registerTool("create-org",{description:"Create an organization and switch this login to it. Later calls act in the new org.",inputSchema:{name:e().min(1)}},async({name:n})=>await d(async()=>await r.createOnboardingOrg(n))),t.registerTool("select-org",{description:"Switch this login to another organization, by an orgId from list-orgs.",inputSchema:{orgId:e().min(1)}},async({orgId:n})=>await d(async()=>await r.selectOnboardingOrg(n))),t.registerTool("list-projects",{description:"Every project in the current org, empty ones last. Project names are not unique, so take the id from here.",inputSchema:{}},async()=>await d(async()=>await r.listProjects())),t.registerTool("list-stages",{description:"Every stage of one project, by project name.",inputSchema:{project:e().min(1)}},async({project:n})=>await d(async()=>await r.listStages(n))),t.registerTool("create-stage",{description:"Create a stage in a project, optionally cloning another stage's architecture and env vars. A project name that does not exist yet is created with it, which is the only way to make a project without deploying a manifest.",inputSchema:{project:e().min(1),name:e().min(1),from:e().optional().describe("Stage to clone architecture and env vars from.")}},async({project:n,name:i,from:c})=>await d(async()=>await r.createStage(n,i,c)))}function q(t,r,n){if(r)t.registerTool("rotate-secret",{description:"Rotate the account key. The current key stops working immediately and the new one is shown once, so this breaks every deployment and CI job still holding the old one. Requires confirm:true.",inputSchema:{confirm:v}},async({confirm:i})=>await d(async()=>(y(i,"Everything holding the current secret breaks the moment this runs."),await r.rotateSecret())));if(n)t.registerTool("delete-project",{description:"Delete a project and everything under it: stages, agents, canvas, env vars, crons and workspace files. Takes a projectId from list-projects and requires confirm:true.",inputSchema:{projectId:e().min(1),confirm:v}},async({projectId:i,confirm:c})=>await d(async()=>(y(c,`Project '${i}' and everything under it goes away: name it, get the owner's agreement, then retry.`),await n.deleteProject(i))))}function N(t,r){t.registerTool("list-cron-runs",{description:"Run history for one cron, newest first. Check this before telling anyone a cron fired.",inputSchema:{cronId:e().min(1),limit:o().int().positive().max(100).optional()}},async({cronId:i,limit:c})=>await d(async()=>await r.listCronRuns(i,c===void 0?{}:{limit:c})));let n=[["suspend-sandbox","Suspend a persistent sandbox reservation.",(i,c)=>r.suspendSandbox(i,c)],["resume-sandbox","Resume a suspended sandbox reservation.",(i,c)=>r.resumeSandbox(i,c)],["terminate-sandbox","Terminate a sandbox reservation and drop its live-instance row. Terminate only a sandbox you created, and never one holding state you did not put there.",(i,c)=>r.terminateSandbox(i,c)],["open-sandbox-terminal","Mint a short-lived sealed ticket for an interactive PTY session on a persistent sandbox.",(i,c)=>r.openSandboxTerminal(i,c)]];for(let[i,c,p]of n)t.registerTool(i,{description:c,inputSchema:M},async({sandboxId:f,reservationKey:g})=>await d(async()=>await p(f,g)));t.registerTool("snapshot-sandbox",{description:"Snapshot a sandbox reservation into a reusable image (sandbox or lambda provider; lambda builds for a few minutes).",inputSchema:{sandboxId:e().min(1),reservationKey:e().min(1),name:e().min(1)}},async({sandboxId:i,reservationKey:c,name:p})=>await d(async()=>await r.snapshotSandbox(i,c,p))),t.registerTool("upload-skill",{description:"Replace one skill's bundle wholesale (PUT, not a merge) by skillName. 'body' is the same source shape create-skill takes.",inputSchema:{id:e().min(1).describe("The skillName."),body:s(e(),l())}},async({id:i,body:c})=>await d(async()=>await r.uploadSkill(i,c))),t.registerTool("list-env-vars",{description:"List environment variable names. Values are write-only: the plane never returns them, so there is no read.",inputSchema:{}},async()=>await d(async()=>await r.listEnvVars())),t.registerTool("set-env-var",{description:"Create or replace one write-only environment variable. Reference it from a config as ${NAME} rather than inlining the value.",inputSchema:{name:e().min(1),value:e()}},async({name:i,value:c})=>await d(async()=>(await r.setEnvVar(i,c),{name:i,stored:!0}))),t.registerTool("delete-env-var",{description:"Delete one environment variable.",inputSchema:{name:e().min(1),confirm:v}},async({name:i,confirm:c})=>await d(async()=>(y(c,`Anything referencing \${${i}} breaks the moment this env var goes.`),{deleted:await r.deleteEnvVar(i),name:i}))),t.registerTool("assume-role",{description:"Exchange a role for a short-lived session token (default 1h, max 12h). Hand the returned token to whatever tool needs narrow access, as BROODS_SESSION_TOKEN. It is shown once and is not stored here.",inputSchema:{roleId:e().min(1),ttlSeconds:o().int().positive().max(43200).optional()}},async({roleId:i,ttlSeconds:c})=>await d(async()=>await r.assumeRole(i,c===void 0?{}:{ttlSeconds:c}))),t.registerTool("update-account",{description:"Rename this account or change its description. Everything else about an account is derived, not settable.",inputSchema:{username:e().optional(),description:e().nullable().optional()}},async({username:i,description:c})=>await d(async()=>await r.updateAccount({...i===void 0?{}:{username:i},...c===void 0?{}:{description:c}}))),t.registerTool("get-account",{description:"The account this server's credential resolves to. Run it first to confirm which tenant you are about to change.",inputSchema:{}},async()=>await d(async()=>await r.getAccount()))}function x(t,r,n,i){let c=e().min(1).describe(`The ${n.key}.`),p=n.scoped?` ${n.plural} live in one stage, so this also takes 'project' and 'stage'.`:"",{list:f,get:g,create:w,update:S,remove:_}=n;if(f){let u=`List every ${n.singular} on the account.${p}`;if(n.scoped)t.registerTool(`list-${n.plural}`,{description:u,inputSchema:E},async({project:h,stage:m})=>await d(async()=>await f(r,T(n,i,h,m))));else t.registerTool(`list-${n.plural}`,{description:u,inputSchema:{}},async()=>await d(async()=>await f(r)))}if(g)t.registerTool(`get-${n.singular}`,{description:`Read one ${n.singular} by ${n.key}.`,inputSchema:{id:c}},async({id:u})=>await d(async()=>await g(r,u)));if(w){let u=`Create one ${n.singular}. 'body' is the request body the config plane documents.`+(n.createHint?` ${n.createHint}`:"")+p+" Prefer changing the broods/ manifest and deploying for anything the project already declares.",h=s(e(),l());if(n.scoped)t.registerTool(`create-${n.singular}`,{description:u,inputSchema:{body:h,...E}},async({body:m,project:I,stage:C})=>await d(async()=>await w(r,m,T(n,i,I,C))));else t.registerTool(`create-${n.singular}`,{description:u,inputSchema:{body:h}},async({body:m})=>await d(async()=>await w(r,m)))}if(S)t.registerTool(`update-${n.singular}`,{description:`Deep-merge a patch into one ${n.singular} and return the updated record, so there is no need to read it back.`+(n.updateHint?` ${n.updateHint}`:""),inputSchema:{id:c,body:s(e(),l())}},async({id:u,body:h})=>await d(async()=>await S(r,u,h)));if(_)t.registerTool(`delete-${n.singular}`,{description:`Delete one ${n.singular}. Requires confirm:true, and takes one ${n.key} per call: never loop this over a list.`+(n.deleteHint?` ${n.deleteHint}`:""),inputSchema:{id:c,confirm:v}},async({id:u,confirm:h})=>await d(async()=>(y(h,`Deleting ${n.singular} '${u}' goes away for good: name it, get the owner's agreement, then retry.`),{deleted:await _(r,u),id:u})))}export{Pt as createBroodsMcpServer};
|
|
1
|
+
import{j}from"./chunk-r86n05ec.js";import{Z,R}from"./chunk-bb3jfhv3.js";import{e,o,a,l,s}from"./chunk-n890pdvc.js";var E={project:e().optional(),stage:e().optional()},M={sandboxId:e().min(1),reservationKey:e().min(1)},v=a().describe("Must be true, after the owner has agreed to this delete."),b="'********' keeps a stored secret and null deletes a field: never send a placeholder you did not read from a get.",A=[{singular:"agent",plural:"agents",key:"agentId",createHint:"Needs name and config; a 409 returns the existing agentId.",deleteHint:"Deleting an agent also drops its runtime rows.",updateHint:b,list:(t)=>t.listAgents(),get:(t,r)=>t.getAgent(r),create:(t,r)=>t.createAgent(r),update:(t,r,n)=>t.updateAgent(r,n),remove:(t,r)=>t.deleteAgent(r)},{singular:"cron",plural:"crons",key:"cronId",createHint:"Needs name, agentId, scheduleExpression (rate/cron/at) and input or events.",list:(t)=>t.listCrons(),get:(t,r)=>t.getCron(r),create:(t,r)=>t.createCron(r),update:(t,r,n)=>t.updateCron(r,n),remove:(t,r)=>t.deleteCron(r)},{singular:"sandbox",plural:"sandboxes",key:"sandboxId",updateHint:b,list:(t)=>t.listSandboxes(),get:(t,r)=>t.getSandbox(r),create:(t,r)=>t.createSandbox(r),update:(t,r,n)=>t.updateSandbox(r,n),remove:(t,r)=>t.deleteSandbox(r)},{singular:"workspace",plural:"workspaces",key:"workspaceId",list:(t)=>t.listWorkspaces(),get:(t,r)=>t.getWorkspace(r),create:(t,r)=>t.createWorkspace(r),update:(t,r,n)=>t.updateWorkspace(r,n),remove:(t,r)=>t.deleteWorkspace(r)},{singular:"policy",plural:"policies",key:"policyId",createHint:"Agent-runtime policy document (tool.call, skill.load), not API credential scoping.",list:(t)=>t.listPolicies(),get:(t,r)=>t.getPolicy(r),create:(t,r)=>t.createPolicy(r),update:(t,r,n)=>t.updatePolicy(r,n),remove:(t,r)=>t.deletePolicy(r)},{singular:"role",plural:"roles",key:"roleId",createHint:"Needs name and a version-1 policy over the API namespace, e.g. agents:read. Account key only.",list:(t)=>t.listRoles(),get:(t,r)=>t.getRole(r),create:(t,r)=>t.createRole(r),update:(t,r,n)=>t.updateRole(r,n),remove:(t,r)=>t.deleteRole(r)},{singular:"channel",plural:"channels",key:"channelId",list:(t)=>t.listChannels(),get:(t,r)=>t.getChannel(r),create:(t,r)=>t.createChannel(r),update:(t,r,n)=>t.updateChannel(r,n),remove:(t,r)=>t.deleteChannel(r)},{singular:"skill",plural:"skills",key:"skillName",createHint:'source is "files" (base64), "json" or "github". The stored name comes from the SKILL.md frontmatter.',list:(t)=>t.listSkills(),get:(t,r)=>t.getSkill(r),create:(t,r)=>t.createSkill(r),remove:(t,r)=>t.deleteSkill(r)},{singular:"mcp",plural:"mcp",key:"serverId",scoped:!0,list:(t,r)=>t.listMcp(r),get:(t,r)=>t.getMcp(r),create:(t,r,n)=>t.createMcp(n,r),update:(t,r,n)=>t.updateMcp(r,n),remove:(t,r)=>t.deleteMcp(r)}];function Pt(t={}){let r=Z()?new R({}):null;if(!r&&!t.cli)throw Error("broods mcp needs a credential: set BROODS_SESSION_TOKEN or BROODS_ACCOUNT_SECRET, or run `broods login`.");let n=new j({name:"broods",version:"1"},{capabilities:{tools:{}}});if(r){for(let i of A)N(n,r,i,t.scope??{});x(n,r)}if(t.cli)O(n,t.cli);if(t.allowDestructive)q(n,r,t.cli??null);return n}function y(t,r){if(!t)throw Error(`Refusing without confirm:true. ${r}`)}async function d(t){try{return P(await t())}catch(r){return k(r)}}function k(t){return{content:[{type:"text",text:t instanceof Error?t.message:String(t)}],isError:!0}}function P(t){return{content:[{type:"text",text:JSON.stringify(t??null,null,2)}]}}function T(t,r,n,i){if(!t.scoped)return;let c=n??r.project,p=i??r.stage;if(!c||!p)throw Error(`${t.plural} live in one stage, so this call needs both 'project' and 'stage'`);return{project:c,stage:p}}function O(t,r){t.registerTool("list-orgs",{description:"Every organization this login can reach, with the current one marked.",inputSchema:{}},async()=>await d(async()=>{let n=await r.getOnboarding();return{currentOrgId:n.currentOrgId,orgs:n.orgs}})),t.registerTool("create-org",{description:"Create an organization and switch this login to it. Later calls act in the new org.",inputSchema:{name:e().min(1)}},async({name:n})=>await d(async()=>await r.createOnboardingOrg(n))),t.registerTool("select-org",{description:"Switch this login to another organization, by an orgId from list-orgs.",inputSchema:{orgId:e().min(1)}},async({orgId:n})=>await d(async()=>await r.selectOnboardingOrg(n))),t.registerTool("list-projects",{description:"Every project in the current org, empty ones last. Project names are not unique, so take the id from here.",inputSchema:{}},async()=>await d(async()=>await r.listProjects())),t.registerTool("list-stages",{description:"Every stage of one project, by project name.",inputSchema:{project:e().min(1)}},async({project:n})=>await d(async()=>await r.listStages(n))),t.registerTool("create-stage",{description:"Create a stage in a project, optionally cloning another stage's architecture and env vars. A project name that does not exist yet is created with it, which is the only way to make a project without deploying a manifest.",inputSchema:{project:e().min(1),name:e().min(1),from:e().optional().describe("Stage to clone architecture and env vars from.")}},async({project:n,name:i,from:c})=>await d(async()=>await r.createStage(n,i,c)))}function q(t,r,n){if(r)t.registerTool("rotate-secret",{description:"Rotate the account key. The current key stops working immediately and the new one is shown once, so this breaks every deployment and CI job still holding the old one. Requires confirm:true.",inputSchema:{confirm:v}},async({confirm:i})=>await d(async()=>(y(i,"Everything holding the current secret breaks the moment this runs."),await r.rotateSecret())));if(n)t.registerTool("delete-project",{description:"Delete a project and everything under it: stages, agents, canvas, env vars, crons and workspace files. Takes a projectId from list-projects and requires confirm:true.",inputSchema:{projectId:e().min(1),confirm:v}},async({projectId:i,confirm:c})=>await d(async()=>(y(c,`Project '${i}' and everything under it goes away: name it, get the owner's agreement, then retry.`),await n.deleteProject(i))))}function x(t,r){t.registerTool("list-cron-runs",{description:"Run history for one cron, newest first. Check this before telling anyone a cron fired.",inputSchema:{cronId:e().min(1),limit:o().int().positive().max(100).optional()}},async({cronId:i,limit:c})=>await d(async()=>await r.listCronRuns(i,c===void 0?{}:{limit:c})));let n=[["suspend-sandbox","Suspend a persistent sandbox reservation.",(i,c)=>r.suspendSandbox(i,c)],["resume-sandbox","Resume a suspended sandbox reservation.",(i,c)=>r.resumeSandbox(i,c)],["terminate-sandbox","Terminate a sandbox reservation and drop its live-instance row. Terminate only a sandbox you created, and never one holding state you did not put there.",(i,c)=>r.terminateSandbox(i,c)],["open-sandbox-terminal","Mint a short-lived sealed ticket for an interactive PTY session on a persistent sandbox.",(i,c)=>r.openSandboxTerminal(i,c)]];for(let[i,c,p]of n)t.registerTool(i,{description:c,inputSchema:M},async({sandboxId:f,reservationKey:g})=>await d(async()=>await p(f,g)));t.registerTool("snapshot-sandbox",{description:"Snapshot a sandbox reservation into an image its provider's sandboxes can pin with `snapshot` (sandbox, lambda, daytona, e2b or vercel; lambda builds for a few minutes).",inputSchema:{sandboxId:e().min(1),reservationKey:e().min(1),name:e().min(1)}},async({sandboxId:i,reservationKey:c,name:p})=>await d(async()=>await r.snapshotSandbox(i,c,p))),t.registerTool("upload-skill",{description:"Replace one skill's bundle wholesale (PUT, not a merge) by skillName. 'body' is the same source shape create-skill takes.",inputSchema:{id:e().min(1).describe("The skillName."),body:s(e(),l())}},async({id:i,body:c})=>await d(async()=>await r.uploadSkill(i,c))),t.registerTool("list-env-vars",{description:"List environment variable names. Values are write-only: the plane never returns them, so there is no read.",inputSchema:{}},async()=>await d(async()=>await r.listEnvVars())),t.registerTool("set-env-var",{description:"Create or replace one write-only environment variable. Reference it from a config as ${NAME} rather than inlining the value.",inputSchema:{name:e().min(1),value:e()}},async({name:i,value:c})=>await d(async()=>(await r.setEnvVar(i,c),{name:i,stored:!0}))),t.registerTool("delete-env-var",{description:"Delete one environment variable.",inputSchema:{name:e().min(1),confirm:v}},async({name:i,confirm:c})=>await d(async()=>(y(c,`Anything referencing \${${i}} breaks the moment this env var goes.`),{deleted:await r.deleteEnvVar(i),name:i}))),t.registerTool("assume-role",{description:"Exchange a role for a short-lived session token (default 1h, max 12h). Hand the returned token to whatever tool needs narrow access, as BROODS_SESSION_TOKEN. It is shown once and is not stored here.",inputSchema:{roleId:e().min(1),ttlSeconds:o().int().positive().max(43200).optional()}},async({roleId:i,ttlSeconds:c})=>await d(async()=>await r.assumeRole(i,c===void 0?{}:{ttlSeconds:c}))),t.registerTool("update-account",{description:"Rename this account or change its description. Everything else about an account is derived, not settable.",inputSchema:{username:e().optional(),description:e().nullable().optional()}},async({username:i,description:c})=>await d(async()=>await r.updateAccount({...i===void 0?{}:{username:i},...c===void 0?{}:{description:c}}))),t.registerTool("get-account",{description:"The account this server's credential resolves to. Run it first to confirm which tenant you are about to change.",inputSchema:{}},async()=>await d(async()=>await r.getAccount()))}function N(t,r,n,i){let c=e().min(1).describe(`The ${n.key}.`),p=n.scoped?` ${n.plural} live in one stage, so this also takes 'project' and 'stage'.`:"",{list:f,get:g,create:w,update:S,remove:_}=n;if(f){let u=`List every ${n.singular} on the account.${p}`;if(n.scoped)t.registerTool(`list-${n.plural}`,{description:u,inputSchema:E},async({project:h,stage:m})=>await d(async()=>await f(r,T(n,i,h,m))));else t.registerTool(`list-${n.plural}`,{description:u,inputSchema:{}},async()=>await d(async()=>await f(r)))}if(g)t.registerTool(`get-${n.singular}`,{description:`Read one ${n.singular} by ${n.key}.`,inputSchema:{id:c}},async({id:u})=>await d(async()=>await g(r,u)));if(w){let u=`Create one ${n.singular}. 'body' is the request body the config plane documents.`+(n.createHint?` ${n.createHint}`:"")+p+" Prefer changing the broods/ manifest and deploying for anything the project already declares.",h=s(e(),l());if(n.scoped)t.registerTool(`create-${n.singular}`,{description:u,inputSchema:{body:h,...E}},async({body:m,project:I,stage:C})=>await d(async()=>await w(r,m,T(n,i,I,C))));else t.registerTool(`create-${n.singular}`,{description:u,inputSchema:{body:h}},async({body:m})=>await d(async()=>await w(r,m)))}if(S)t.registerTool(`update-${n.singular}`,{description:`Deep-merge a patch into one ${n.singular} and return the updated record, so there is no need to read it back.`+(n.updateHint?` ${n.updateHint}`:""),inputSchema:{id:c,body:s(e(),l())}},async({id:u,body:h})=>await d(async()=>await S(r,u,h)));if(_)t.registerTool(`delete-${n.singular}`,{description:`Delete one ${n.singular}. Requires confirm:true, and takes one ${n.key} per call: never loop this over a list.`+(n.deleteHint?` ${n.deleteHint}`:""),inputSchema:{id:c,confirm:v}},async({id:u,confirm:h})=>await d(async()=>(y(h,`Deleting ${n.singular} '${u}' goes away for good: name it, get the owner's agreement, then retry.`),{deleted:await _(r,u),id:u})))}export{Pt as createBroodsMcpServer};
|
package/dist/cli/index.js
CHANGED
|
@@ -86,8 +86,8 @@ ${o.map((s)=>` ${pn(t?.[s]??s)}: ids.${e}[${JSON.stringify(s)}],`).join(`
|
|
|
86
86
|
Check the browser tab and the dashboard logs for an error. If the browser shows
|
|
87
87
|
404 on /cli-auth/start, deploy the dashboard build that includes CLI auth or pass
|
|
88
88
|
--dashboard-url for the environment you deployed. Other common causes are missing
|
|
89
|
-
cliAuth Convex functions or no active API account (
|
|
90
|
-
`)}}async function _e(e,n,t,o){if(n.length===0)throw Error(`${e}: no options available`);if(n.length===1)return n[0];let r=o!==void 0&&o>=0&&o<n.length?o:void 0;console.log(e),n.forEach((a,c)=>{console.log(Se(`${c+1}. ${t(a)}`,c===r))});let s=r===void 0?`Choose 1-${n.length}: `:`Choose 1-${n.length} [${r+1}]: `,i=Ie({input:ne,output:ee});try{for(;;){let a=(await i.question(s)).trim();if(a===""&&r!==void 0)return n[r];let c=Number(a);if(Number.isInteger(c)&&c>=1&&c<=n.length)return n[c-1];console.log(`Enter a number from 1 to ${n.length}.`)}}finally{i.close()}}async function _t(e,n,t,o){console.log(e),n.forEach((i,a)=>{console.log(` ${a+1}. ${t(i)}`)});let r=n.length>0?`Choose 1-${n.length}, or `:"",s=Ie({input:ne,output:ee});try{let i=(await s.question(`${r}${o.hint} [${o.defaultValue}]: `)).trim();if(i==="")return o.defaultValue;let a=Number(i);if(Number.isInteger(a)&&a>=1&&a<=n.length)return n[a-1];return i}finally{s.close()}}async function be(e,n){if(!ne.isTTY)return n??"";let t=Ie({input:ne,output:ee});try{let o=t.question(`${e}: `);if(n){if(ne.isTTY&&ee.isTTY)ee.write("\x1B[90m");if(t.write(n),ne.isTTY&&ee.isTTY)ee.write("\x1B[0m")}return(await o).trim()||n||""}finally{t.close()}}function Sn(e){let{command:n,args:t}=ds(e),o=is(n,t,{stdio:"ignore",detached:!0});o.on("error",(r)=>{console.error(je(`Could not open a browser (${r.message}). Open the URL above to continue.`,{stream:"stderr"}))}),o.unref()}function $n(e,n){return new Promise((t,o)=>{let r=ts((c,d)=>{let l=new URL(c.url??"/","http://127.0.0.1");if(l.pathname!==n.path||l.searchParams.get("state")!==e){d.writeHead(400).end("Invalid callback.");return}try{let p=n.read(l.searchParams);d.writeHead(200,{"Content-Type":"text/plain"}).end(n.done),s(p)}catch(p){d.writeHead(400,{"Content-Type":"text/plain"}).end(p instanceof Error?p.message:String(p)),i(p)}}),s,i,a=new Promise((c,d)=>{s=c,i=d});r.on("error",(c)=>{if(!n.fixedPort&&n.port!==0&&c.code==="EADDRINUSE"){r.listen(0,"127.0.0.1");return}o(c)}),r.listen(n.port,"127.0.0.1",()=>{let c=r.address();if(!c||typeof c==="string"){o(Error("Failed to allocate callback port"));return}t({code:{callbackUrl:`http://127.0.0.1:${c.port}${n.path}`,promise:a},close:()=>r.close()})})})}async function Cn(e,n=ls){let t,o=new Promise((r,s)=>{t=setTimeout(()=>s(Error(n)),cs)});try{return await Promise.race([e,o])}finally{if(t)clearTimeout(t)}}async function us(e){let n=await qe(e,{method:"GET",redirect:"manual"});if(n.status===404){let t=new URL(e);throw Error(`${t.origin} has no broods login page. Check that --dashboard-url points at your broods dashboard.`)}if(n.status>=500)throw Error(`The broods login page failed: ${n.status} ${await n.text()}`)}function gs(){let e=process.env.BROODS_LOGIN_PORT;if(e){let n=Number(e);if(Number.isInteger(n)&&n>0&&n<65536)return n;throw Error("BROODS_LOGIN_PORT must be a TCP port number")}return 18987}function ps(e){let n=e.get("error");if(n)throw Error(`Login failed: ${n}`);let t=e.get("code");if(!t)throw Error("Login callback carried no code.");let o=e.get("base_url");if(!o)throw Error("Login callback did not advertise the API base URL (base_url). Deploy a dashboard build that includes the Convex-direct CLI auth flow.");return{code:t,baseUrl:f(o)}}var hs=1455,ws="/auth/callback";async function Ut(e,n,t=Sn){let o=he[n].label,r=Dt(),s=Dt(),i=ms(32).toString("base64url"),{code:a,close:c}=await $n(r,{port:hs,fixedPort:!1,path:ws,read:bs,done:`${o} is connected to broods. You can close this tab.`});try{let d=a.callbackUrl,l=await e.startConnection(n,{redirectUri:d,codeChallenge:fs("sha256").update(i).digest("base64url"),state:r,nonce:s});t(l.authorizeUrl),console.log(`Opening ${l.authorizeUrl}`);let p=await Cn(a.promise,`Timed out waiting for the ${o} sign-in to finish in the browser.`);return await e.connect(n,{code:p.code,codeVerifier:i,redirectUri:d,nonce:s,clientId:p.clientId??l.clientId,hostId:l.hostId})}finally{c()}}function bs(e){let n=e.get("error");if(n)throw Error(`Sign-in failed: ${e.get("error_description")??n}`);let t=e.get("code");if(!t)throw Error("Sign-in callback carried no code.");return{code:t,clientId:e.get("client_id")??void 0}}import{existsSync as Lt}from"node:fs";import{mkdir as vs,readFile as ks,writeFile as Es}from"node:fs/promises";import{dirname as Mt,join as An,resolve as Ss,sep as ge}from"node:path";import{fileURLToPath as $s}from"node:url";var De={name:"broods",version:"0.
|
|
89
|
+
cliAuth Convex functions or no active API account (Organization settings -> API access).`,jt=new Set(["--base-url","--cwd","--dashboard-url","--stage","--from","--level","--limit","--mcp","--project","--region","--sandbox","-n"]);function ds(e,n=process.platform){if(n==="darwin")return{command:"open",args:[e]};if(n==="win32")return{command:"rundll32",args:["url.dll,FileProtocolHandler",e]};return{command:"xdg-open",args:[e]}}function I(e,n){let t=jt.has(n)?`${n}=`:null;return e.some((o)=>o===n||t!==null&&o.startsWith(t))}function we(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function b(e,n){for(let t=0;t<e.length;t+=1){let o=e[t];if(o===n)return e[t+1];if(o?.startsWith(`${n}=`))return o.slice(n.length+1)||void 0}return}function te(e){let n=[];for(let t=0;t<e.length;t+=1){let o=e[t];if(o===void 0)continue;if(jt.has(o)){t+=1;continue}if(o.startsWith("-"))continue;n.push(o)}return n}async function En(e){let n=ss(),t=rs(32).toString("base64url"),o=os("sha256").update(t).digest("base64url"),{code:r,close:s}=await $n(n,{port:gs(),fixedPort:Boolean(process.env.BROODS_LOGIN_PORT),path:"/callback",read:ps,done:"broods CLI login complete. You can close this tab."});try{let i=r.callbackUrl,a=`${f(e)}/cli-auth/start?`+new URLSearchParams({callback:i,state:n,code_challenge:o});await us(a),console.log(`Opening ${a}`),Sn(a);let c=await Cn(r.promise),d=await qe(`${c.baseUrl}/v1/account/auth/exchange`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:c.code,code_verifier:t})});if(!d.ok)throw Error(`Login exchange failed: ${d.status} ${await d.text()}`);let l=await d.json(),p={baseUrl:c.baseUrl,dashboardUrl:f(e),token:l.token,createdAt:new Date().toISOString(),...l.user?{user:l.user}:{},...l.org?{org:l.org}:{},...l.account?{account:l.account}:{}};return await W(p),p}finally{s()}}async function q(e){h();let n=T(e);if(!n){let t=process.env.BROODS_DASHBOARD_URL,o=e??process.env.BROODS_BASE_URL??(t?L(t):void 0);throw Error(o?`Not logged in to ${f(o)}. Run \`broods login\`.`:"Run `broods login` first, or set BROODS_TOKEN and BROODS_BASE_URL.")}return n}async function Qe(e){if(!ne.isTTY)return!1;let n=Ie({input:ne,output:ee});try{let t=(await n.question(`${e} [y/N] `)).trim().toLowerCase();return t==="y"||t==="yes"}finally{n.close()}}async function It(e){ee.write(`${e}: `);let n=new as({write:(o,r,s)=>s()}),t=Ie({input:ne,output:n,terminal:ne.isTTY===!0});try{let o=await t.question("");if(!o)throw Error(`${e} is required`);return o}finally{t.close(),ee.write(`
|
|
90
|
+
`)}}async function _e(e,n,t,o){if(n.length===0)throw Error(`${e}: no options available`);if(n.length===1)return n[0];let r=o!==void 0&&o>=0&&o<n.length?o:void 0;console.log(e),n.forEach((a,c)=>{console.log(Se(`${c+1}. ${t(a)}`,c===r))});let s=r===void 0?`Choose 1-${n.length}: `:`Choose 1-${n.length} [${r+1}]: `,i=Ie({input:ne,output:ee});try{for(;;){let a=(await i.question(s)).trim();if(a===""&&r!==void 0)return n[r];let c=Number(a);if(Number.isInteger(c)&&c>=1&&c<=n.length)return n[c-1];console.log(`Enter a number from 1 to ${n.length}.`)}}finally{i.close()}}async function _t(e,n,t,o){console.log(e),n.forEach((i,a)=>{console.log(` ${a+1}. ${t(i)}`)});let r=n.length>0?`Choose 1-${n.length}, or `:"",s=Ie({input:ne,output:ee});try{let i=(await s.question(`${r}${o.hint} [${o.defaultValue}]: `)).trim();if(i==="")return o.defaultValue;let a=Number(i);if(Number.isInteger(a)&&a>=1&&a<=n.length)return n[a-1];return i}finally{s.close()}}async function be(e,n){if(!ne.isTTY)return n??"";let t=Ie({input:ne,output:ee});try{let o=t.question(`${e}: `);if(n){if(ne.isTTY&&ee.isTTY)ee.write("\x1B[90m");if(t.write(n),ne.isTTY&&ee.isTTY)ee.write("\x1B[0m")}return(await o).trim()||n||""}finally{t.close()}}function Sn(e){let{command:n,args:t}=ds(e),o=is(n,t,{stdio:"ignore",detached:!0});o.on("error",(r)=>{console.error(je(`Could not open a browser (${r.message}). Open the URL above to continue.`,{stream:"stderr"}))}),o.unref()}function $n(e,n){return new Promise((t,o)=>{let r=ts((c,d)=>{let l=new URL(c.url??"/","http://127.0.0.1");if(l.pathname!==n.path||l.searchParams.get("state")!==e){d.writeHead(400).end("Invalid callback.");return}try{let p=n.read(l.searchParams);d.writeHead(200,{"Content-Type":"text/plain"}).end(n.done),s(p)}catch(p){d.writeHead(400,{"Content-Type":"text/plain"}).end(p instanceof Error?p.message:String(p)),i(p)}}),s,i,a=new Promise((c,d)=>{s=c,i=d});r.on("error",(c)=>{if(!n.fixedPort&&n.port!==0&&c.code==="EADDRINUSE"){r.listen(0,"127.0.0.1");return}o(c)}),r.listen(n.port,"127.0.0.1",()=>{let c=r.address();if(!c||typeof c==="string"){o(Error("Failed to allocate callback port"));return}t({code:{callbackUrl:`http://127.0.0.1:${c.port}${n.path}`,promise:a},close:()=>r.close()})})})}async function Cn(e,n=ls){let t,o=new Promise((r,s)=>{t=setTimeout(()=>s(Error(n)),cs)});try{return await Promise.race([e,o])}finally{if(t)clearTimeout(t)}}async function us(e){let n=await qe(e,{method:"GET",redirect:"manual"});if(n.status===404){let t=new URL(e);throw Error(`${t.origin} has no broods login page. Check that --dashboard-url points at your broods dashboard.`)}if(n.status>=500)throw Error(`The broods login page failed: ${n.status} ${await n.text()}`)}function gs(){let e=process.env.BROODS_LOGIN_PORT;if(e){let n=Number(e);if(Number.isInteger(n)&&n>0&&n<65536)return n;throw Error("BROODS_LOGIN_PORT must be a TCP port number")}return 18987}function ps(e){let n=e.get("error");if(n)throw Error(`Login failed: ${n}`);let t=e.get("code");if(!t)throw Error("Login callback carried no code.");let o=e.get("base_url");if(!o)throw Error("Login callback did not advertise the API base URL (base_url). Deploy a dashboard build that includes the Convex-direct CLI auth flow.");return{code:t,baseUrl:f(o)}}var hs=1455,ws="/auth/callback";async function Ut(e,n,t=Sn){let o=he[n].label,r=Dt(),s=Dt(),i=ms(32).toString("base64url"),{code:a,close:c}=await $n(r,{port:hs,fixedPort:!1,path:ws,read:bs,done:`${o} is connected to broods. You can close this tab.`});try{let d=a.callbackUrl,l=await e.startConnection(n,{redirectUri:d,codeChallenge:fs("sha256").update(i).digest("base64url"),state:r,nonce:s});t(l.authorizeUrl),console.log(`Opening ${l.authorizeUrl}`);let p=await Cn(a.promise,`Timed out waiting for the ${o} sign-in to finish in the browser.`);return await e.connect(n,{code:p.code,codeVerifier:i,redirectUri:d,nonce:s,clientId:p.clientId??l.clientId,hostId:l.hostId})}finally{c()}}function bs(e){let n=e.get("error");if(n)throw Error(`Sign-in failed: ${e.get("error_description")??n}`);let t=e.get("code");if(!t)throw Error("Sign-in callback carried no code.");return{code:t,clientId:e.get("client_id")??void 0}}import{existsSync as Lt}from"node:fs";import{mkdir as vs,readFile as ks,writeFile as Es}from"node:fs/promises";import{dirname as Mt,join as An,resolve as Ss,sep as ge}from"node:path";import{fileURLToPath as $s}from"node:url";var De={name:"broods",version:"0.49.0",description:"CLI + TypeScript SDK for the broods agent platform.",keywords:["agents","ai","broods","cli","sdk"],homepage:"https://github.com/beeblastco/broods#readme",bugs:{url:"https://github.com/beeblastco/broods/issues"},license:"MIT",repository:{type:"git",url:"git+https://github.com/beeblastco/broods.git",directory:"packages/broods"},bin:{broods:"dist/cli/index.js"},files:["dist","README.md","LICENSE.md"],type:"module",main:"./dist/index.js",types:"./dist/index.d.ts",typesVersions:{"*":{account:["./dist/account.d.ts"]}},exports:{".":{types:"./dist/index.d.ts",import:"./dist/index.js"},"./account":{types:"./dist/account.d.ts",import:"./dist/account.js"}},scripts:{build:"bun scripts/build.ts && rollup -c rollup.dts.config.mjs && rm -rf .dts-temp",check:"tsc --noEmit","pack:check":"bun run build && publint && attw --pack .",prepublishOnly:"bun run build",test:"bun test"},dependencies:{"@modelcontextprotocol/client":"^2.0.0",esbuild:"^0.28.2"},devDependencies:{"@ai-sdk/tui":"1.0.128","@arethetypeswrong/cli":"^0.18.5","@chat-adapter/discord":"^4.38.1","@chat-adapter/github":"^4.38.1","@chat-adapter/slack":"^4.38.1","@chat-adapter/telegram":"^4.38.1","@modelcontextprotocol/server":"2.0.0","@types/bun":"^1.3.14","@typescript/typescript6":"6.0.2",ai:"^7.0.127",publint:"^0.3.23",rollup:"^4.62.4","rollup-plugin-dts":"^6.5.1",typescript:"^7.0.2",zod:"^4.4.3"},peerDependencies:{ai:"^7.0.127"},engines:{node:">=22.15"}};var Rn=An(Mt(O),"update-check.json"),Cs=86400000,Rs="https://registry.npmjs.org/broods/latest",Os=2000,As=[["bun.lock","bun"],["bun.lockb","bun"],["pnpm-lock.yaml","pnpm"],["yarn.lock","yarn"]];function xn(e,n){let t=On(e),o=On(n);for(let r=0;r<3;r+=1){let s=t[r]??0,i=o[r]??0;if(s!==i)return s>i}return!Nt(e)&&Nt(n)}async function Pn(e={}){let n=await Ps(),t=e.maxAgeMs??Cs;if(n&&Date.now()-n.checkedAt<t)return n.latest;let o=await xs();if(o===null)return n?.latest??null;return await js({checkedAt:Date.now(),latest:o}),o}function Ue(e){return On(e)[0]??0}function Bt(e=Ss(process.cwd()),n=$s(import.meta.url),t=De.version){let o=`${ge}node_modules${ge}`,r=n.indexOf(o),s=r===-1?null:n.slice(0,r),i=s===null?n.startsWith(`${e}${ge}`):e===s||e.startsWith(`${s}${ge}`),a=n.includes(`${ge}.bun${ge}install${ge}global${ge}`),c=s??e,d=i?Ts(c):a?"bun":"npm",l=d==="npm"?"install":"add",p=d==="pnpm"&&Lt(An(c,"pnpm-workspace.yaml"))?["-w"]:[],u=`broods@${Ue(t)}`;return{manager:d,global:!i,command:d,cwd:i?c:e,args:i?[l,...p,u]:[l,"-g",u]}}async function xs(){try{let e=await fetch(Rs,{signal:AbortSignal.timeout(Os),headers:{accept:"application/json"}});if(!e.ok)return null;let n=await e.json();return typeof n.version==="string"?n.version:null}catch{return null}}function Nt(e){return e.includes("-")}async function Ps(){try{let e=JSON.parse(await ks(Rn,"utf8"));if(typeof e.latest!=="string")return null;if(typeof e.checkedAt!=="number")return null;return{checkedAt:e.checkedAt,latest:e.latest}}catch{return null}}function Ts(e){return As.find(([t])=>Lt(An(e,t)))?.[1]??"npm"}function On(e){let[n]=e.trim().replace(/^v/,"").split("-");return(n??"").split(".").slice(0,3).map((t)=>Number.parseInt(t,10)||0)}async function js(e){try{await vs(Mt(Rn),{recursive:!0}),await Es(Rn,`${JSON.stringify(e)}
|
|
91
91
|
`,"utf8")}catch{}}var Gt=`---
|
|
92
92
|
name: broods
|
|
93
93
|
description: Work on a broods account alongside a developer from Claude Code or a development agent. Covers onboarding and sign-in, creating a broods project, defining and deploying agents and their resources with the CLI, and integrating a deployed agent into existing code with the SDK. Use when asked to set up broods, create or work on a broods project, create or manage agents or any account resource, or call a deployed agent from application code.
|
|
@@ -400,7 +400,7 @@ ${Z("stage")}`);let i=h(),a=b(e,"--dashboard-url")??i.dashboardUrl??oe,c=rn(e),d
|
|
|
400
400
|
|
|
401
401
|
${Z("env")}`);if(a&&!t)throw Error(`broods env ${n} needs a variable name.
|
|
402
402
|
|
|
403
|
-
${Z("env")}`);let{manifest:c,config:d}=await le({project:b(e,"--project"),stage:b(e,"--stage"),command:"dev"}),l=await q(b(e,"--base-url")??d.baseUrl),p=new N({baseUrl:l.baseUrl,token:l.token}),u=`${c.project}/${c.stage}`;if(i){await Li(p,c,u);return}if(o){let C=await p.listEnv(c.project,c.stage);if(C.length===0){console.log(`No environment variables set for ${u}.`);return}console.log(`Environment variables for ${u} (values hidden):`);for(let k of C)console.log(` ${k.name}`);return}if(s){let C=await p.getEnv(c.project,c.stage,t);if(C===null){console.error(`${t} is not set for ${u}`),process.exitCode=1;return}console.log(C);return}if(r){await p.removeEnv(c.project,c.stage,t),H(`Removed ${t} from ${u}`);return}let S=await It(t);await p.setEnv(c.project,c.stage,t,S),H(`Stored ${t} for ${u}`)}async function Li(e,n,t){let o=await Ln(e,n);if(o.length===0){console.log('No env("NAME") references in this project.');return}let r=await io(e,n,o);if(r.length>0)kn(r);let s=nn(o,"synced").length;console.log(r.length>0?`${s} other referenced variable(s) already in step with ${t}.`:`${s} referenced variable(s) already in step with ${t}.`);let i=nn(o,"stage-only");if(i.length>0)console.log(`${i.length} referenced variable(s) live only on ${t}: ${i.join(", ")}`);let a=nn(o,"unresolved");if(a.length>0)x(`${a.length} referenced variable(s) with no value here or on ${t}: ${a.join(", ")}. Put them in .env.local, or run \`broods env set <NAME> --stage ${n.stage}\`.`)}function Mn(e){if(I(e,"--all"))return"DEBUG";let n=b(e,"--level");if(n===void 0&&I(e,"--level"))throw Error("Missing value for --level. Use DEBUG, INFO, WARN, or ERROR.");if(!n)return"WARN";let t=n.toUpperCase();if(t==="DEBUG")return"DEBUG";if(t==="INFO")return"INFO";if(t==="WARN"||t==="WARNING")return"WARN";if(t==="ERROR")return"ERROR";throw Error(`Unknown log level: ${n}. Use DEBUG, INFO, WARN, or ERROR.`)}async function Mi(e){h();let n=b(e,"--project")??process.env.BROODS_PROJECT,t=b(e,"--stage")??v();if(!n)throw Error("Project name is required. Pass --project <name> or set BROODS_PROJECT in .env.local.");if(!t)throw Error("Stage name is required. Pass --stage <name> or set BROODS_STAGE in .env.local.");return{project:n,stage:t}}async function sn(e){let n=await Mi(e),t=h(),o=await Be(b(e,"--dashboard-url")??t.dashboardUrl??oe,b(e,"--base-url")),r=new N({baseUrl:o.baseUrl,token:o.token}),s=await r.mintStageSession(n.project,n.stage),i=async()=>{if(s.expiresAt-Date.now()<Vs)s=await r.mintStageSession(n.project,n.stage);return s.token};return{baseUrl:process.env.BROODS_BASE_URL??process.env.BROODS_HOST??E,credential:i,project:s.projectSlug,stage:s.stageSlug}}function ao(e){return e==="WARN"?" (--all for INFO too)":""}function Bn(e,n){console.error(je(`Reconnecting to live logs (attempt ${e}): ${n}`,{stream:"stderr"}))}function Gn(e){let n=new Date(e.ts).toISOString().slice(11,23),t=e.level.padEnd(5),o=Hs(e.message);return`${n} ${t} ${e.eventType} ${o}`}async function Bi(e){let n=await sn(e),{project:t,stage:o}=n,r=Mn(e),s=new AbortController,i=()=>s.abort();process.on("SIGINT",i),console.log(`Streaming live logs for ${t}/${o} [${r}+]`+ao(r)+", Ctrl+C to stop");try{for await(let a of Ve(n,{backfill:0,minLevel:r,signal:s.signal,onReconnect:Bn}))console.log(Gn(a))}catch(a){if(!s.signal.aborted)console.error(a instanceof Error?a.message:String(a)),process.exitCode=1}finally{process.off("SIGINT",i)}}async function Gi(e){if(I(e,"--doctor")){await Wi(I(e,"--request"));return}let n=te(e)[0];if(!n){console.log(ve("machine",e));return}let{runMachineDaemon:t}=await import("../chunk-
|
|
403
|
+
${Z("env")}`);let{manifest:c,config:d}=await le({project:b(e,"--project"),stage:b(e,"--stage"),command:"dev"}),l=await q(b(e,"--base-url")??d.baseUrl),p=new N({baseUrl:l.baseUrl,token:l.token}),u=`${c.project}/${c.stage}`;if(i){await Li(p,c,u);return}if(o){let C=await p.listEnv(c.project,c.stage);if(C.length===0){console.log(`No environment variables set for ${u}.`);return}console.log(`Environment variables for ${u} (values hidden):`);for(let k of C)console.log(` ${k.name}`);return}if(s){let C=await p.getEnv(c.project,c.stage,t);if(C===null){console.error(`${t} is not set for ${u}`),process.exitCode=1;return}console.log(C);return}if(r){await p.removeEnv(c.project,c.stage,t),H(`Removed ${t} from ${u}`);return}let S=await It(t);await p.setEnv(c.project,c.stage,t,S),H(`Stored ${t} for ${u}`)}async function Li(e,n,t){let o=await Ln(e,n);if(o.length===0){console.log('No env("NAME") references in this project.');return}let r=await io(e,n,o);if(r.length>0)kn(r);let s=nn(o,"synced").length;console.log(r.length>0?`${s} other referenced variable(s) already in step with ${t}.`:`${s} referenced variable(s) already in step with ${t}.`);let i=nn(o,"stage-only");if(i.length>0)console.log(`${i.length} referenced variable(s) live only on ${t}: ${i.join(", ")}`);let a=nn(o,"unresolved");if(a.length>0)x(`${a.length} referenced variable(s) with no value here or on ${t}: ${a.join(", ")}. Put them in .env.local, or run \`broods env set <NAME> --stage ${n.stage}\`.`)}function Mn(e){if(I(e,"--all"))return"DEBUG";let n=b(e,"--level");if(n===void 0&&I(e,"--level"))throw Error("Missing value for --level. Use DEBUG, INFO, WARN, or ERROR.");if(!n)return"WARN";let t=n.toUpperCase();if(t==="DEBUG")return"DEBUG";if(t==="INFO")return"INFO";if(t==="WARN"||t==="WARNING")return"WARN";if(t==="ERROR")return"ERROR";throw Error(`Unknown log level: ${n}. Use DEBUG, INFO, WARN, or ERROR.`)}async function Mi(e){h();let n=b(e,"--project")??process.env.BROODS_PROJECT,t=b(e,"--stage")??v();if(!n)throw Error("Project name is required. Pass --project <name> or set BROODS_PROJECT in .env.local.");if(!t)throw Error("Stage name is required. Pass --stage <name> or set BROODS_STAGE in .env.local.");return{project:n,stage:t}}async function sn(e){let n=await Mi(e),t=h(),o=await Be(b(e,"--dashboard-url")??t.dashboardUrl??oe,b(e,"--base-url")),r=new N({baseUrl:o.baseUrl,token:o.token}),s=await r.mintStageSession(n.project,n.stage),i=async()=>{if(s.expiresAt-Date.now()<Vs)s=await r.mintStageSession(n.project,n.stage);return s.token};return{baseUrl:process.env.BROODS_BASE_URL??process.env.BROODS_HOST??E,credential:i,project:s.projectSlug,stage:s.stageSlug}}function ao(e){return e==="WARN"?" (--all for INFO too)":""}function Bn(e,n){console.error(je(`Reconnecting to live logs (attempt ${e}): ${n}`,{stream:"stderr"}))}function Gn(e){let n=new Date(e.ts).toISOString().slice(11,23),t=e.level.padEnd(5),o=Hs(e.message);return`${n} ${t} ${e.eventType} ${o}`}async function Bi(e){let n=await sn(e),{project:t,stage:o}=n,r=Mn(e),s=new AbortController,i=()=>s.abort();process.on("SIGINT",i),console.log(`Streaming live logs for ${t}/${o} [${r}+]`+ao(r)+", Ctrl+C to stop");try{for await(let a of Ve(n,{backfill:0,minLevel:r,signal:s.signal,onReconnect:Bn}))console.log(Gn(a))}catch(a){if(!s.signal.aborted)console.error(a instanceof Error?a.message:String(a)),process.exitCode=1}finally{process.off("SIGINT",i)}}async function Gi(e){if(I(e,"--doctor")){await Wi(I(e,"--request"));return}let n=te(e)[0];if(!n){console.log(ve("machine",e));return}let{runMachineDaemon:t}=await import("../chunk-084dph9c.js"),{baseUrl:o,credential:r}=await sn(e),s=P(b(e,"--cwd")??process.cwd()),i=I(e,"--computer"),a=b(e,"--mcp"),c=I(e,"--force"),d=new AbortController,l=()=>d.abort();process.on("SIGINT",l),console.log(`Connecting ${n} to ${o} (cwd ${s}${i?", computer use on":""}), Ctrl+C to stop`);try{await t({baseUrl:o,computer:i,credential:r,cwd:s,force:c,log:(p)=>console.log(p),mcpFile:a?P(a):void 0,sandbox:n,signal:d.signal})}catch(p){console.error(p instanceof Error?p.message:String(p)),process.exitCode=1}finally{process.off("SIGINT",l)}}async function Wi(e){try{let{startDesktop:n}=await import("../chunk-5tnnqapc.js"),{display:t,driver:o,permissions:r}=await n(e);o.stop();let s=(i)=>i?"granted":"missing";if(console.log(`screenshot frame ${t.width}x${t.height} (scale ${t.scale.toFixed(2)})`),console.log(`screen recording ${s(r.screenRecording)}`),console.log(`accessibility ${s(r.accessibility)}`),!r.screenRecording||!r.accessibility)console.log(e?"Approve the prompts, or add this terminal under System Settings > Privacy & Security, then run again.":"Run `broods machine --doctor --request` to show the macOS prompts."),process.exitCode=1}catch(n){console.error(n instanceof Error?n.message:String(n)),process.exitCode=1}}async function Fi(e){let n=await sn(e),{project:t,stage:o}=n,r=b(e,"--sandbox");if(I(e,"--sandbox")&&!G(r))throw Error("--sandbox needs the instance log id, the UUID shown in the dashboard Instances sheet.");let s=r?"DEBUG":Mn(e),i=Number(b(e,"--limit")??b(e,"-n")??100),a=I(e,"--json"),c=new AbortController,d=()=>c.abort();process.on("SIGINT",d),console.log(`Logs for ${t}/${o}`+(r?` sandbox ${r}`:"")+` [${s}+]`+ao(s)+` (backfill ${i}), Ctrl+C to stop`);try{for await(let l of Ve(n,{backfill:i,minLevel:s,...r?{sandboxId:r}:{},signal:c.signal,onReconnect:Bn}))if(a)console.log(JSON.stringify(l));else console.log(Gn(l))}catch(l){if(!c.signal.aborted)console.error(l instanceof Error?l.message:String(l)),process.exitCode=1}finally{process.off("SIGINT",d)}}async function Ki(e){let[n,t]=te(e);if(!n){console.log(ve("agent",e));return}if(n==="list"||n==="ls"){await Hi(e);return}if(n==="get"){await Vi(t,e);return}throw Error(`Unknown agent subcommand: ${n}
|
|
404
404
|
|
|
405
405
|
${Z("agent")}`)}async function co(e){let{manifest:n,config:t}=await le({project:b(e,"--project"),stage:b(e,"--stage"),command:"dev"}),o=await q(b(e,"--base-url")??t.baseUrl),r=await new N({baseUrl:o.baseUrl,token:o.token}).getManifest(n.project,n.stage),s=n.resources.filter((i)=>i.kind==="agent").map((i)=>({name:i.name,config:i.config??{},agentId:r?.ids.agents[i.name]}));return{manifest:n,agents:s}}async function Hi(e){let{manifest:n,agents:t}=await co(e);if(t.length===0){console.log(`No agents declared in ${n.project}/${n.stage}.`);return}console.log(`Agents in ${n.project}/${n.stage}:`);for(let o of t){let r=lo(o.config),s=o.config.publicAccess===!0?"public":"private",i=o.agentId?o.agentId:"not deployed";console.log(` ${o.name} [${s}] ${r} (${i})`)}}async function Vi(e,n){if(!e)throw Error(`Missing agent name.
|
|
406
406
|
|
|
@@ -449,4 +449,4 @@ export const myAgent = defineAgent({
|
|
|
449
449
|
// only reachable via internal endpoints or channel webhooks.
|
|
450
450
|
publicAccess: true,
|
|
451
451
|
});
|
|
452
|
-
`}function qi(){if("bun"in process.versions)return;if(typeof Ms.registerHooks==="function")return;throw Error(`broods needs Node ${De.engines.node.replace(">=","")} or newer, or Bun. This is Node ${process.version}.`)}function Zi(e,n){if(e==="status")throw Error("status was renamed to whoami. Run `broods whoami`.");if(I(n,"--env")||b(n,"--env")!==void 0)throw Error("--env was renamed to --stage. Pass --stage <name> instead.");h();let t=process.env.BROODS_ENVIRONMENT;if(t!==void 0&&process.env.BROODS_STAGE===void 0)throw Error(`BROODS_ENVIRONMENT was renamed to BROODS_STAGE. Rename the key in .env.local (it is currently set to "${t}").`)}async function Xi(){let e=h(),n=T(),[{createBroodsMcpServer:t},{serveStdio:o}]=await Promise.all([import("../chunk-
|
|
452
|
+
`}function qi(){if("bun"in process.versions)return;if(typeof Ms.registerHooks==="function")return;throw Error(`broods needs Node ${De.engines.node.replace(">=","")} or newer, or Bun. This is Node ${process.version}.`)}function Zi(e,n){if(e==="status")throw Error("status was renamed to whoami. Run `broods whoami`.");if(I(n,"--env")||b(n,"--env")!==void 0)throw Error("--env was renamed to --stage. Pass --stage <name> instead.");h();let t=process.env.BROODS_ENVIRONMENT;if(t!==void 0&&process.env.BROODS_STAGE===void 0)throw Error(`BROODS_ENVIRONMENT was renamed to BROODS_STAGE. Rename the key in .env.local (it is currently set to "${t}").`)}async function Xi(){let e=h(),n=T(),[{createBroodsMcpServer:t},{serveStdio:o}]=await Promise.all([import("../chunk-bbgvyp29.js"),import("../chunk-kjccsy1h.js")]),r=n?new N({baseUrl:n.baseUrl,token:n.token}):null,s=t({allowDestructive:process.env.BROODS_MCP_ALLOW_DESTRUCTIVE==="1"&&M("BROODS_MCP_ALLOW_DESTRUCTIVE"),cli:r,scope:{project:e.project,stage:e.stage}});o(()=>s),await new Promise((i)=>{process.stdin.on("close",i)})}Zs().catch((e)=>{At(e instanceof Error?e.message:String(e)),process.exit(1)});export{se,fe,D};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { SystemModelMessage, LanguageModelCallOptions, RequestOptions, streamText, JSONSchema7, ModelMessage, TextStreamPart, ToolSet } from 'ai';
|
|
2
|
+
import * as convex_values from 'convex/values';
|
|
3
|
+
import { Infer } from 'convex/values';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Account-owned code hook upload validation for Convex config-plane sync.
|
|
@@ -795,7 +797,6 @@ interface SandboxConfig {
|
|
|
795
797
|
onCreate?: string[];
|
|
796
798
|
onResume?: string[];
|
|
797
799
|
timeout?: number;
|
|
798
|
-
memoryLimit?: number;
|
|
799
800
|
outputLimitBytes?: number;
|
|
800
801
|
envVars?: Record<string, undefined | string>;
|
|
801
802
|
options?: Record<string, unknown>;
|
|
@@ -914,55 +915,114 @@ interface WorkspaceConfig {
|
|
|
914
915
|
}
|
|
915
916
|
|
|
916
917
|
/**
|
|
917
|
-
*
|
|
918
|
-
*
|
|
919
|
-
*
|
|
918
|
+
* Versioned policy document accepted by account-management CRUD, as the
|
|
919
|
+
* `agentPolicies` and `accountRoles` tables store it. `mode` says how hard
|
|
920
|
+
* the policy bites where it is attached; omitted reads as `audit`.
|
|
920
921
|
*/
|
|
921
|
-
declare const
|
|
922
|
-
|
|
923
|
-
* API action namespace for account roles: one read/write pair per config-plane
|
|
924
|
-
* resource route. Roles carry the same PolicyDocument shape as agent policies;
|
|
925
|
-
* each caller passes its action set to `normalizePolicyDocument`.
|
|
926
|
-
*/
|
|
927
|
-
declare const API_POLICY_ACTIONS: readonly ["account:read", "account:write", "agents:read", "agents:write", "audit:read", "audit:write", "channels:read", "channels:write", "crons:read", "crons:write", "env:read", "env:write", "hooks:read", "hooks:write", "mcp:read", "mcp:write", "policies:read", "policies:write", "sandboxes:read", "sandboxes:write", "skills:read", "skills:write", "workspaces:read", "workspaces:write"];
|
|
928
|
-
type AgentPolicyAction = (typeof AGENT_POLICY_ACTIONS)[number];
|
|
929
|
-
type ApiPolicyAction = (typeof API_POLICY_ACTIONS)[number];
|
|
930
|
-
type PolicyAction = AgentPolicyAction | ApiPolicyAction;
|
|
931
|
-
interface PolicyCondition {
|
|
932
|
-
attribute: string;
|
|
933
|
-
operator: PolicyConditionOperator;
|
|
934
|
-
value: string | number | boolean | string[] | number[] | boolean[];
|
|
935
|
-
}
|
|
936
|
-
type PolicyConditionOperator = "equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains";
|
|
937
|
-
/**
|
|
938
|
-
* Versioned policy document accepted by account-management CRUD.
|
|
939
|
-
*/
|
|
940
|
-
interface PolicyDocument {
|
|
922
|
+
declare const policyDocumentValidator: convex_values.VObject<{
|
|
923
|
+
mode?: "enforce" | "audit" | undefined;
|
|
941
924
|
version: 1;
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
925
|
+
rules: {
|
|
926
|
+
resources?: {
|
|
927
|
+
toolNames?: string[] | undefined;
|
|
928
|
+
mcpIds?: string[] | undefined;
|
|
929
|
+
workspaceIds?: string[] | undefined;
|
|
930
|
+
workspaceNames?: string[] | undefined;
|
|
931
|
+
filePaths?: string[] | undefined;
|
|
932
|
+
subagentIds?: string[] | undefined;
|
|
933
|
+
skillPaths?: string[] | undefined;
|
|
934
|
+
resourceIds?: string[] | undefined;
|
|
935
|
+
} | undefined;
|
|
936
|
+
conditions?: {
|
|
937
|
+
attribute: string;
|
|
938
|
+
operator: "equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains";
|
|
939
|
+
value: string | number | boolean | string[] | number[] | boolean[];
|
|
940
|
+
}[] | undefined;
|
|
941
|
+
id: string;
|
|
942
|
+
effect: "allow" | "deny";
|
|
943
|
+
actions: string[];
|
|
944
|
+
}[];
|
|
945
|
+
}, {
|
|
946
|
+
version: convex_values.VLiteral<1, "required">;
|
|
947
|
+
mode: convex_values.VUnion<"enforce" | "audit" | undefined, [convex_values.VLiteral<"enforce", "required">, convex_values.VLiteral<"audit", "required">], "optional", never>;
|
|
948
|
+
rules: convex_values.VArray<{
|
|
949
|
+
resources?: {
|
|
950
|
+
toolNames?: string[] | undefined;
|
|
951
|
+
mcpIds?: string[] | undefined;
|
|
952
|
+
workspaceIds?: string[] | undefined;
|
|
953
|
+
workspaceNames?: string[] | undefined;
|
|
954
|
+
filePaths?: string[] | undefined;
|
|
955
|
+
subagentIds?: string[] | undefined;
|
|
956
|
+
skillPaths?: string[] | undefined;
|
|
957
|
+
resourceIds?: string[] | undefined;
|
|
958
|
+
} | undefined;
|
|
959
|
+
conditions?: {
|
|
960
|
+
attribute: string;
|
|
961
|
+
operator: "equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains";
|
|
962
|
+
value: string | number | boolean | string[] | number[] | boolean[];
|
|
963
|
+
}[] | undefined;
|
|
964
|
+
id: string;
|
|
965
|
+
effect: "allow" | "deny";
|
|
966
|
+
actions: string[];
|
|
967
|
+
}[], convex_values.VObject<{
|
|
968
|
+
resources?: {
|
|
969
|
+
toolNames?: string[] | undefined;
|
|
970
|
+
mcpIds?: string[] | undefined;
|
|
971
|
+
workspaceIds?: string[] | undefined;
|
|
972
|
+
workspaceNames?: string[] | undefined;
|
|
973
|
+
filePaths?: string[] | undefined;
|
|
974
|
+
subagentIds?: string[] | undefined;
|
|
975
|
+
skillPaths?: string[] | undefined;
|
|
976
|
+
resourceIds?: string[] | undefined;
|
|
977
|
+
} | undefined;
|
|
978
|
+
conditions?: {
|
|
979
|
+
attribute: string;
|
|
980
|
+
operator: "equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains";
|
|
981
|
+
value: string | number | boolean | string[] | number[] | boolean[];
|
|
982
|
+
}[] | undefined;
|
|
983
|
+
id: string;
|
|
984
|
+
effect: "allow" | "deny";
|
|
985
|
+
actions: string[];
|
|
986
|
+
}, {
|
|
987
|
+
id: convex_values.VString<string, "required">;
|
|
988
|
+
effect: convex_values.VUnion<"allow" | "deny", [convex_values.VLiteral<"allow", "required">, convex_values.VLiteral<"deny", "required">], "required", never>;
|
|
989
|
+
actions: convex_values.VArray<string[], convex_values.VString<string, "required">, "required">;
|
|
990
|
+
resources: convex_values.VObject<{
|
|
991
|
+
toolNames?: string[] | undefined;
|
|
992
|
+
mcpIds?: string[] | undefined;
|
|
993
|
+
workspaceIds?: string[] | undefined;
|
|
994
|
+
workspaceNames?: string[] | undefined;
|
|
995
|
+
filePaths?: string[] | undefined;
|
|
996
|
+
subagentIds?: string[] | undefined;
|
|
997
|
+
skillPaths?: string[] | undefined;
|
|
998
|
+
resourceIds?: string[] | undefined;
|
|
999
|
+
} | undefined, {
|
|
1000
|
+
toolNames: convex_values.VArray<string[] | undefined, convex_values.VString<string, "required">, "optional">;
|
|
1001
|
+
/** MCP registration ids, for scoping tool.call rules per server (#331). */
|
|
1002
|
+
mcpIds: convex_values.VArray<string[] | undefined, convex_values.VString<string, "required">, "optional">;
|
|
1003
|
+
workspaceIds: convex_values.VArray<string[] | undefined, convex_values.VString<string, "required">, "optional">;
|
|
1004
|
+
workspaceNames: convex_values.VArray<string[] | undefined, convex_values.VString<string, "required">, "optional">;
|
|
1005
|
+
filePaths: convex_values.VArray<string[] | undefined, convex_values.VString<string, "required">, "optional">;
|
|
1006
|
+
subagentIds: convex_values.VArray<string[] | undefined, convex_values.VString<string, "required">, "optional">;
|
|
1007
|
+
skillPaths: convex_values.VArray<string[] | undefined, convex_values.VString<string, "required">, "optional">;
|
|
1008
|
+
resourceIds: convex_values.VArray<string[] | undefined, convex_values.VString<string, "required">, "optional">;
|
|
1009
|
+
}, "optional", "toolNames" | "mcpIds" | "workspaceIds" | "workspaceNames" | "filePaths" | "subagentIds" | "skillPaths" | "resourceIds">;
|
|
1010
|
+
conditions: convex_values.VArray<{
|
|
1011
|
+
attribute: string;
|
|
1012
|
+
operator: "equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains";
|
|
1013
|
+
value: string | number | boolean | string[] | number[] | boolean[];
|
|
1014
|
+
}[] | undefined, convex_values.VObject<{
|
|
1015
|
+
attribute: string;
|
|
1016
|
+
operator: "equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains";
|
|
1017
|
+
value: string | number | boolean | string[] | number[] | boolean[];
|
|
1018
|
+
}, {
|
|
1019
|
+
attribute: convex_values.VString<string, "required">;
|
|
1020
|
+
operator: convex_values.VUnion<"equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains", convex_values.VLiteral<"equals" | "notEquals" | "in" | "notIn" | "prefix" | "contains", "required">[], "required", never>;
|
|
1021
|
+
value: convex_values.VUnion<string | number | boolean | string[] | number[] | boolean[], [convex_values.VString<string, "required">, convex_values.VFloat64<number, "required">, convex_values.VBoolean<boolean, "required">, convex_values.VArray<string[], convex_values.VString<string, "required">, "required">, convex_values.VArray<number[], convex_values.VFloat64<number, "required">, "required">, convex_values.VArray<boolean[], convex_values.VBoolean<boolean, "required">, "required">], "required", never>;
|
|
1022
|
+
}, "required", "attribute" | "operator" | "value">, "optional">;
|
|
1023
|
+
}, "required", "id" | "effect" | "actions" | "resources" | "conditions" | "resources.toolNames" | "resources.mcpIds" | "resources.workspaceIds" | "resources.workspaceNames" | "resources.filePaths" | "resources.subagentIds" | "resources.skillPaths" | "resources.resourceIds">, "required">;
|
|
1024
|
+
}, "required", "version" | "mode" | "rules">;
|
|
1025
|
+
type PolicyDocument = Infer<typeof policyDocumentValidator>;
|
|
966
1026
|
|
|
967
1027
|
/**
|
|
968
1028
|
* Channel record validation for the config plane, the single home of the
|
|
@@ -2378,7 +2438,7 @@ declare class BroodsAccountClient {
|
|
|
2378
2438
|
resumeSandbox(sandboxId: string, reservationKey: string): Promise<SandboxLifecycleResult>;
|
|
2379
2439
|
/** Terminate a persistent sandbox reservation and drop its live-instance row. Throws on 404/403/409. */
|
|
2380
2440
|
terminateSandbox(sandboxId: string, reservationKey: string): Promise<SandboxLifecycleResult>;
|
|
2381
|
-
/** Snapshot a persistent sandbox reservation into
|
|
2441
|
+
/** Snapshot a persistent sandbox reservation into an image any sandbox of that provider can pin with `snapshot` (`sandbox`, `daytona`, `e2b`, `vercel`, or `lambda`, which answers `building` until its image build lands). Throws on 404/403/409. */
|
|
2382
2442
|
snapshotSandbox(sandboxId: string, reservationKey: string, name: string): Promise<SandboxSnapshotResult>;
|
|
2383
2443
|
/** Mint a short-lived sealed ticket for an interactive PTY session on a persistent sandbox (`sandbox`/`lambda`/`cloudflare` providers). Throws on 404/403/409. */
|
|
2384
2444
|
openSandboxTerminal(sandboxId: string, reservationKey: string): Promise<SandboxTerminalTicket>;
|
package/package.json
CHANGED
package/dist/chunk-11g7dgvw.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{U,z}from"./chunk-hb3c3xt9.js";import{e,o,a,l,n,t,d,C,s,p,r}from"./chunk-n890pdvc.js";import{se,fe,D}from"./cli/index.js";import{spawn as te}from"node:child_process";import{hostname as oe}from"node:os";import{performance as L}from"node:perf_hooks";var N=["cursor_position","double_click","hold_key","key","left_click","left_click_drag","left_mouse_down","left_mouse_up","middle_click","mouse_move","right_click","screenshot","scroll","triple_click","type","wait","zoom"];var _={badFrame:{code:4400,reason:"Malformed frame"},occupied:{code:4423,reason:"Already connected from another daemon"},replaced:{code:4409,reason:"Replaced by a newer connection"},unauthorized:{code:4401,reason:"Unauthorized; run broods login"},unknownSandbox:{code:4404,reason:"No machine sandbox with that name in this account"}};var H="/v1/machines/ws",P=t({action:p(N),coordinate:n(o().int()).length(2).optional().describe("[x, y] in screenshot pixels for mouse actions."),start_coordinate:n(o().int()).length(2).optional().describe("[x, y] where a left_click_drag starts."),text:e().optional().describe("Text for type, the key chord for key and hold_key, or modifiers to hold during a click, drag or scroll."),scroll_direction:p(["down","left","right","up"]).optional(),scroll_amount:o().int().min(1).optional().describe("Scroll clicks, default 3."),duration:o().min(0).max(300).optional().describe("Seconds for wait and hold_key."),region:n(o().int()).length(4).optional().describe("[x0, y0, x1, y1] of the screenshot to zoom into."),repeat:o().int().min(1).max(100).optional().describe("How many times to press the key.")}),B=P.extend({type:r("computer"),id:e()}),W=t({type:r("computer-result"),id:e(),text:e().optional(),image:t({data:e(),mediaType:e()}).optional(),app:e().optional(),error:e().optional()}),G=t({type:r("exec"),id:e(),code:e(),cwd:e().optional(),env:s(e(),e()).optional(),timeoutSeconds:o().positive(),outputLimitBytes:o().positive()}),J=t({type:r("hello"),sandbox:e().min(1),hostname:e().optional(),platform:e().optional(),computer:a().optional(),mcp:n(e()).optional(),instance:e().optional(),force:a().optional()}),K=t({type:r("mcp-call"),id:e(),server:e(),tool:e(),args:s(e(),l())}),X=t({type:r("mcp-list"),id:e(),server:e()}),Y=t({type:r("mcp-result"),id:e(),result:d({content:n(l())}).optional(),error:e().optional()}),q=t({type:r("mcp-tools"),id:e(),tools:n(d({name:e(),inputSchema:s(e(),l())})).optional(),error:e().optional()}),Z=t({type:r("ready"),sandboxId:e()}),Q=t({type:r("result"),id:e(),exitCode:o().nullable(),stdout:e(),stderr:e(),durationMs:o(),timedOut:a(),truncated:a()}),V=C("type",[B,G,K,X,Z]),ue=C("type",[W,J,Y,q,Q]);function T(i){let c=new URL(H,i);return c.protocol=c.protocol==="https:"?"wss:":"ws:",c.toString()}function R(i){return ee(V,i)}function ee(i,c){if(typeof c!=="string")return null;try{let u=i.safeParse(JSON.parse(c));return u.success?u.data:null}catch{return null}}var re=new Set([_.occupied.code,_.replaced.code,_.unauthorized.code,_.unknownSandbox.code]),A=72,k=30000,I=1000;function ne(i,c,u){return new Promise((f)=>{let m=L.now(),h=new E(i.outputLimitBytes),w=new E(i.outputLimitBytes),b=!1,x=!1,M=(y,O)=>{if(x)return;if(x=!0,clearTimeout(v),u?.removeEventListener("abort",g),O)w.append(Buffer.from(`${O}
|
|
2
|
-
`));f({type:"result",id:i.id,exitCode:y,stdout:h.text(),stderr:w.text(),durationMs:Math.round(L.now()-m),timedOut:b,truncated:h.truncated||w.truncated})},F=te("bash",["-lc",i.code],{cwd:i.cwd??c,env:{...U(),...i.env},detached:!0,stdio:["ignore","pipe","pipe"]}),S=()=>{if(!F.pid)return;try{process.kill(-F.pid,"SIGKILL")}catch{F.kill("SIGKILL")}},v=setTimeout(()=>{b=!0,S()},i.timeoutSeconds*1000),g=()=>{S(),M(null,"stopped: the daemon closed its socket")};if(u?.addEventListener("abort",g,{once:!0}),F.stdout.on("data",(y)=>h.append(y)),F.stderr.on("data",(y)=>w.append(y)),F.on("error",(y)=>M(null,y.message)),F.on("close",(y)=>M(y)),u?.aborted)g()})}async function we(i){let c=fe(),u=crypto.randomUUID(),f=i.mcpFile?await ce(i.mcpFile,i.log):null,m=i.computer?await ae(i.log):null,h=I;try{while(!i.signal.aborted){let w=Date.now(),b;try{b=await i.credential()}catch(M){if(M instanceof D)throw M;i.log(`stage session unavailable (${M instanceof Error?M.message:String(M)}), retrying in ${Math.round(h/1000)}s`),await se(h,i.signal),h=Math.min(h*2,k);continue}let x=await pe(i,b,u,c,m,f);if(i.signal.aborted)return;if(re.has(x.code))throw Error(x.reason||`core closed the socket (${x.code})`);if(Date.now()-w>k)h=I;i.log(`disconnected (${x.reason||x.code}), reconnecting in ${Math.round(h/1000)}s`),await se(h,i.signal),h=Math.min(h*2,k)}}finally{m?.stop(),await f?.stop()}}class E{#o=[];#e;#t=0;truncated=!1;constructor(i){this.#e=i}append(i){if(this.#t>=this.#e){this.truncated=!0;return}let c=this.#e-this.#t;if(i.byteLength>c){this.#o.push(i.subarray(0,c)),this.#t=this.#e,this.truncated=!0;return}this.#o.push(i),this.#t+=i.byteLength}text(){let i=Buffer.concat(this.#o).toString("utf8");return this.truncated?`${i}
|
|
3
|
-
[output truncated]`:i}}function ie(i){let c=[i.action];if(i.coordinate)c.push(`at ${i.coordinate.join(",")}`);if(i.text!==void 0)c.push(JSON.stringify(j(i.text)));if(i.region)c.push(`region ${i.region.join(",")}`);return c.join(" ")}function j(i){let c=i.trim().split(`
|
|
4
|
-
`)[0]??"";return c.length>A?`${c.slice(0,A)}…`:c}async function ae(i){let{startDesktop:c}=await import("./chunk-5tnnqapc.js"),{display:u,driver:f,permissions:m}=await c(!1);if(i(`computer use on, ${u.width}x${u.height} screenshots`),!m.screenRecording||!m.accessibility)i(" screen recording or accessibility is missing, run `broods machine --doctor --request`");return f}async function ce(i,c){let{McpHost:u,readMcpServersFile:f}=await import("./chunk-46v60rbx.js"),m=new u(f(i),c);return c(`mcp servers from ${i}: ${m.names().join(", ")}`),m}async function le(i,c,u){try{if(i.type==="mcp-list"){let m=await c.listTools(i.server);return u(` mcp ${i.server}: ${m.length} tools`),{type:"mcp-tools",id:i.id,tools:m}}u(` mcp ${i.server}.${i.tool}`);let f=await c.callTool(i.server,i.tool,i.args);return{type:"mcp-result",id:i.id,result:f}}catch(f){let m=f instanceof Error?f.message:String(f);return u(` mcp ${i.server}: ${m}`),{type:i.type==="mcp-call"?"mcp-result":"mcp-tools",id:i.id,error:m}}}function pe(i,c,u,f,m,h){return new Promise((w)=>{let b=new f(T(i.baseUrl),z(c)),x=new AbortController,M=()=>b.close(1000,"daemon stopped"),F=(v)=>{if(b.readyState===b.OPEN)b.send(JSON.stringify(v))},S=(v,g)=>{i.signal.removeEventListener("abort",M),x.abort(),w({code:v,reason:g})};i.signal.addEventListener("abort",M,{once:!0}),b.onopen=()=>F({type:"hello",sandbox:i.sandbox,hostname:oe(),platform:process.platform,computer:m!==null,mcp:h?.names(),instance:u,force:i.force}),b.onmessage=(v)=>{let g=R(v.data);if(g?.type==="ready"){i.log(`connected as ${i.sandbox} (${g.sandboxId})`);return}if(g?.type==="computer"&&m){i.log(` ${ie(g)}`),m.run(g).then((y)=>{if(y.error)i.log(` error: ${y.error}`);F(y)});return}if((g?.type==="mcp-call"||g?.type==="mcp-list")&&h){le(g,h,i.log).then(F);return}if(g?.type!=="exec")return;i.log(`$ ${j(g.code)}`),ne(g,i.cwd,x.signal).then((y)=>{i.log(` exit ${y.exitCode??"none"} in ${y.durationMs}ms${y.timedOut?" (timed out)":""}`),F(y)})},b.onerror=()=>S(1006,"connection failed"),b.onclose=(v)=>S(v.code,v.reason)})}export{we as runMachineDaemon};
|