1use std::env;
2use std::path::{Path, PathBuf};
3
4use anyhow::{Context, Result};
5
6#[derive(Debug, Clone)]
7pub struct BeamPaths {
8 root: PathBuf,
9}
10
11impl BeamPaths {
12 pub fn from_root(root: impl Into<PathBuf>) -> Self {
13 Self { root: root.into() }
14 }
15
16 pub fn discover() -> Result<Self> {
17 if let Ok(root) = env::var("BEAM_HOME") {
18 return Ok(Self::from_root(root));
19 }
20
21 let home =
22 env::var("HOME").context("HOME is not set and BEAM_HOME was not provided")?;
23 Ok(Self::from_root(Path::new(&home).join(".beam")))
24 }
25
26 pub fn root(&self) -> &Path {
27 &self.root
28 }
29
30 pub fn run_dir(&self) -> PathBuf {
31 self.root.join("run")
32 }
33
34 pub fn logs_dir(&self) -> PathBuf {
35 self.root.join("logs")
36 }
37
38 pub fn state_dir(&self) -> PathBuf {
39 self.root.join("state")
40 }
41
42 pub fn sessions_dir(&self) -> PathBuf {
43 self.root.join("sessions")
44 }
45
46 pub fn workflows_dir(&self) -> PathBuf {
47 self.root.join("workflows")
48 }
49
50 pub fn workflow_runs_dir(&self) -> PathBuf {
51 self.workflows_dir().join("runs")
52 }
53
54 pub fn workflow_run_dir(&self, run_id: &str) -> PathBuf {
55 self.workflow_runs_dir().join(run_id)
56 }
57
58 pub fn config_toml(&self) -> PathBuf {
59 self.root.join("config.toml")
60 }
61
62 pub fn bots_json(&self) -> PathBuf {
63 self.root.join("bots.json")
64 }
65
66 pub fn connectors_json(&self) -> PathBuf {
67 self.root.join("connectors.json")
68 }
69
70 pub fn webhook_master_key(&self) -> PathBuf {
71 self.root.join("webhook-master.key")
72 }
73
74 pub fn webhook_secrets_json(&self) -> PathBuf {
75 self.root.join("webhook-secrets.json")
76 }
77
78 pub fn trigger_logs_jsonl(&self) -> PathBuf {
79 self.root.join("trigger-logs.jsonl")
80 }
81
82 pub fn schedules_json(&self) -> PathBuf {
83 self.root.join("schedules.json")
84 }
85
86 pub fn observed_bots_dir(&self) -> PathBuf {
87 self.root.join("observed-bots")
88 }
89
90 pub fn webhook_triggers_json(&self) -> PathBuf {
91 self.root.join("webhook-triggers.json")
92 }
93
94 pub fn webhook_lifecycle_json(&self) -> PathBuf {
95 self.root.join("webhook-lifecycle.json")
96 }
97
98 pub fn schedules_output_dir(&self) -> PathBuf {
99 self.root.join("schedules-output")
100 }
101
102 pub fn runtime_state_json(&self) -> PathBuf {
103 self.run_dir().join("daemon.json")
104 }
105
106 pub fn daemon_log(&self) -> PathBuf {
107 self.logs_dir().join("daemon.log")
108 }
109
110 pub fn session_store_json(&self) -> PathBuf {
111 self.sessions_dir().join("sessions.json")
112 }
113
114 pub fn frozen_cards_dir(&self) -> PathBuf {
115 self.sessions_dir().join("frozen-cards")
116 }
117
118 pub fn frozen_cards_json(&self, session_id: &str) -> PathBuf {
119 self.frozen_cards_dir().join(format!("{}.json", session_id))
120 }
121
122 pub fn pending_response_patches_dir(&self) -> PathBuf {
123 self.sessions_dir().join("pending-response-patches")
124 }
125
126 pub fn pending_response_patch_json(&self, session_id: &str) -> PathBuf {
127 self.pending_response_patches_dir()
128 .join(format!("{}.json", session_id))
129 }
130
131 pub fn workflow_approval_cards_dir(&self) -> PathBuf {
132 self.sessions_dir().join("workflow-approval-cards")
133 }
134
135 pub fn workflow_approval_cards_json(&self, run_id: &str) -> PathBuf {
136 self.workflow_approval_cards_dir()
137 .join(format!("{}.json", run_id))
138 }
139
140 pub fn attempt_resume_dir(&self, run_id: &str, activity_id: &str, attempt_id: &str) -> PathBuf {
141 self.workflow_run_dir(run_id)
142 .join("attempts")
143 .join(activity_id)
144 .join(attempt_id)
145 .join("resumes")
146 }
147
148 pub fn attempt_resume_json(
149 &self,
150 run_id: &str,
151 activity_id: &str,
152 attempt_id: &str,
153 resume_id: &str,
154 ) -> PathBuf {
155 self.attempt_resume_dir(run_id, activity_id, attempt_id)
156 .join(resume_id)
157 .join("resume.json")
158 }
159
160 pub fn worker_init_json(&self, session_id: &str) -> PathBuf {
161 self.run_dir()
162 .join(format!("worker-init-{}.json", session_id))
163 }
164
165 pub fn worker_wrapper_sh(&self, session_id: &str) -> PathBuf {
166 self.run_dir()
167 .join(format!("worker-wrapper-{}.sh", session_id))
168 }
169
170 pub fn cli_pid_markers_dir(&self) -> PathBuf {
171 self.state_dir().join(".beam-cli-pids")
172 }
173}