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 = env::var("HOME").context("HOME is not set and BEAM_HOME was not provided")?;
22 Ok(Self::from_root(Path::new(&home).join(".beam")))
23 }
24
25 pub fn root(&self) -> &Path {
26 &self.root
27 }
28
29 pub fn run_dir(&self) -> PathBuf {
30 self.root.join("run")
31 }
32
33 pub fn logs_dir(&self) -> PathBuf {
34 self.root.join("logs")
35 }
36
37 pub fn state_dir(&self) -> PathBuf {
38 self.root.join("state")
39 }
40
41 pub fn sessions_dir(&self) -> PathBuf {
42 self.root.join("sessions")
43 }
44
45 pub fn workflows_dir(&self) -> PathBuf {
46 self.root.join("workflows")
47 }
48
49 pub fn workflow_runs_dir(&self) -> PathBuf {
50 self.workflows_dir().join("runs")
51 }
52
53 pub fn workflow_run_dir(&self, run_id: &str) -> PathBuf {
54 self.workflow_runs_dir().join(run_id)
55 }
56
57 pub fn config_toml(&self) -> PathBuf {
58 self.root.join("config.toml")
59 }
60
61 pub fn bots_json(&self) -> PathBuf {
62 self.root.join("bots.json")
63 }
64
65 pub fn connectors_json(&self) -> PathBuf {
66 self.root.join("connectors.json")
67 }
68
69 pub fn webhook_master_key(&self) -> PathBuf {
70 self.root.join("webhook-master.key")
71 }
72
73 pub fn webhook_secrets_json(&self) -> PathBuf {
74 self.root.join("webhook-secrets.json")
75 }
76
77 pub fn trigger_logs_jsonl(&self) -> PathBuf {
78 self.root.join("trigger-logs.jsonl")
79 }
80
81 pub fn schedules_json(&self) -> PathBuf {
82 self.root.join("schedules.json")
83 }
84
85 pub fn observed_bots_dir(&self) -> PathBuf {
86 self.root.join("observed-bots")
87 }
88
89 pub fn webhook_triggers_json(&self) -> PathBuf {
90 self.root.join("webhook-triggers.json")
91 }
92
93 pub fn webhook_lifecycle_json(&self) -> PathBuf {
94 self.root.join("webhook-lifecycle.json")
95 }
96
97 pub fn schedules_output_dir(&self) -> PathBuf {
98 self.root.join("schedules-output")
99 }
100
101 pub fn runtime_state_json(&self) -> PathBuf {
102 self.run_dir().join("daemon.json")
103 }
104
105 pub fn daemon_log(&self) -> PathBuf {
106 self.logs_dir().join("daemon.log")
107 }
108
109 pub fn session_store_json(&self) -> PathBuf {
110 self.sessions_dir().join("sessions.json")
111 }
112
113 pub fn frozen_cards_dir(&self) -> PathBuf {
114 self.sessions_dir().join("frozen-cards")
115 }
116
117 pub fn frozen_cards_json(&self, session_id: &str) -> PathBuf {
118 self.frozen_cards_dir().join(format!("{}.json", session_id))
119 }
120
121 pub fn pending_response_patches_dir(&self) -> PathBuf {
122 self.sessions_dir().join("pending-response-patches")
123 }
124
125 pub fn pending_response_patch_json(&self, session_id: &str) -> PathBuf {
126 self.pending_response_patches_dir()
127 .join(format!("{}.json", session_id))
128 }
129
130 pub fn workflow_approval_cards_dir(&self) -> PathBuf {
131 self.sessions_dir().join("workflow-approval-cards")
132 }
133
134 pub fn workflow_approval_cards_json(&self, run_id: &str) -> PathBuf {
135 self.workflow_approval_cards_dir()
136 .join(format!("{}.json", run_id))
137 }
138
139 pub fn attempt_resume_dir(&self, run_id: &str, activity_id: &str, attempt_id: &str) -> PathBuf {
140 self.workflow_run_dir(run_id)
141 .join("attempts")
142 .join(activity_id)
143 .join(attempt_id)
144 .join("resumes")
145 }
146
147 pub fn attempt_resume_json(
148 &self,
149 run_id: &str,
150 activity_id: &str,
151 attempt_id: &str,
152 resume_id: &str,
153 ) -> PathBuf {
154 self.attempt_resume_dir(run_id, activity_id, attempt_id)
155 .join(resume_id)
156 .join("resume.json")
157 }
158
159 pub fn worker_init_json(&self, session_id: &str) -> PathBuf {
160 self.run_dir()
161 .join(format!("worker-init-{}.json", session_id))
162 }
163
164 pub fn worker_wrapper_sh(&self, session_id: &str) -> PathBuf {
165 self.run_dir()
166 .join(format!("worker-wrapper-{}.sh", session_id))
167 }
168
169 pub fn cli_pid_markers_dir(&self) -> PathBuf {
170 self.state_dir().join(".beam-cli-pids")
171 }
172
173 pub fn zellij_web_tokens_json(&self) -> PathBuf {
174 self.state_dir().join("zellij-web-tokens.json")
175 }
176
177 pub fn workflow_progress_cards_json(&self) -> PathBuf {
178 self.state_dir().join("workflow-progress-cards.json")
179 }
180
181 pub fn used_tickets_json(&self) -> PathBuf {
182 self.state_dir().join("used-tickets.json")
183 }
184
185 pub fn ask_pending_json(&self) -> PathBuf {
186 self.state_dir().join("ask-pending.json")
187 }
188
189 pub fn grant_pending_json(&self) -> PathBuf {
190 self.state_dir().join("grant-pending.json")
191 }
192
193 pub fn pending_creates_json(&self) -> PathBuf {
194 self.state_dir().join("pending-creates.json")
195 }
196
197 pub fn replay_nonces_json(&self) -> PathBuf {
198 self.state_dir().join("replay-nonces.json")
199 }
200
201 pub fn rate_buckets_json(&self) -> PathBuf {
202 self.state_dir().join("rate-buckets.json")
203 }
204
205 pub fn recent_lark_events_json(&self) -> PathBuf {
206 self.state_dir().join("recent-lark-events.json")
207 }
208
209 pub fn final_output_retries_json(&self) -> PathBuf {
210 self.state_dir().join("final-output-retries.json")
211 }
212
213 pub fn recent_dirs_json(&self) -> PathBuf {
214 self.state_dir().join("recent-dirs.json")
215 }
216}