@agent-beam/beam 0.2.28 → 0.2.30

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.
@@ -0,0 +1,263 @@
1
+ // Settings for `beam monitor`, so a plain `beam monitor` in a systemd unit or a container needs no
2
+ // flags. Layered like git or npm config, lowest to highest:
3
+ //
4
+ // managed /etc/beam/monitor.json (macOS /Library/Application Support/Beam, Windows %ProgramData%\Beam):
5
+ // set by IT through MDM or config management. It can lock settings, which nothing
6
+ // below can then change.
7
+ // user ~/.beam/monitor.json (or $BEAM_HOME), 0600. The only file that may store a key.
8
+ // project beam.monitor.json in the directory beam monitor starts in (or --config <file>):
9
+ // committed with the app, so keys are refused there.
10
+ // environment variables, then command-line flags.
11
+ //
12
+ // Each top-level setting (files, app, maxCpuPercent, llm) comes whole from the highest layer that
13
+ // sets it, so a project can't half-override a managed LLM setup. `beam monitor configure` shows
14
+ // where every effective value came from.
15
+ //
16
+ // It's created the first time a monitor command runs after an upgrade, carrying over older
17
+ // BEAM_LLM_* settings. It never copies a key's value from the environment, only the name of the
18
+ // variable that holds it. A key stored with `beam monitor configure --key-stdin` lives here, so
19
+ // the file is written 0600, like Beam's other config files.
20
+ import { randomUUID } from "node:crypto";
21
+ import { constants } from "node:fs";
22
+ import { mkdir, open, rename, rm } from "node:fs/promises";
23
+ import { join, resolve } from "node:path";
24
+ import { getBeamHome } from "./config.js";
25
+ export const monitorConfigPath = () => join(getBeamHome(), "monitor.json");
26
+ export const DEFAULT_MONITOR_CONFIG = {
27
+ version: 1, files: [], app: null, maxCpuPercent: 25, llm: { provider: "beam", model: null, baseUrl: null, apiKeyEnv: null },
28
+ };
29
+ const invalid = (why) => new Error(`${monitorConfigPath()} is invalid (${why}). Fix it, or run 'beam monitor configure' to rewrite it.`);
30
+ const isRecord = (v) => !!v && typeof v === "object" && !Array.isArray(v);
31
+ const optionalString = (v, max) => v === null || v === undefined || (typeof v === "string" && v.length <= max);
32
+ export function validateMonitorConfig(value) {
33
+ if (!isRecord(value) || value.version !== 1)
34
+ throw invalid("unsupported version");
35
+ const llm = isRecord(value.llm) ? value.llm : {};
36
+ if (!Array.isArray(value.files ?? []) || !(value.files ?? []).every(f => typeof f === "string" && f.length <= 4096))
37
+ throw invalid("files must be a list of paths");
38
+ if (!optionalString(value.app, 4096))
39
+ throw invalid("app must be a path");
40
+ const cpu = value.maxCpuPercent ?? 25;
41
+ if (typeof cpu !== "number" || !(cpu >= 1 && cpu <= 100))
42
+ throw invalid("maxCpuPercent must be a number from 1 to 100");
43
+ if (typeof (llm.provider ?? "beam") !== "string" || !/^[a-z]{2,20}$/.test(String(llm.provider ?? "beam")))
44
+ throw invalid("llm.provider must be a provider name");
45
+ if (!optionalString(llm.model, 120) || (typeof llm.model === "string" && !/^[\w.:/@-]+$/.test(llm.model)))
46
+ throw invalid("llm.model must be a model name");
47
+ if (!optionalString(llm.baseUrl, 2000))
48
+ throw invalid("llm.baseUrl must be a URL");
49
+ if (!optionalString(llm.apiKeyEnv, 100) || (typeof llm.apiKeyEnv === "string" && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(llm.apiKeyEnv)))
50
+ throw invalid("llm.apiKeyEnv must be a variable name");
51
+ if (llm.apiKey !== undefined && (typeof llm.apiKey !== "string" || !llm.apiKey.trim() || /\s/.test(llm.apiKey) || llm.apiKey.length > 4096))
52
+ throw invalid("llm.apiKey must be a single key");
53
+ return {
54
+ version: 1,
55
+ files: [...(value.files ?? [])],
56
+ app: value.app ?? null,
57
+ maxCpuPercent: cpu,
58
+ llm: {
59
+ provider: String(llm.provider ?? "beam"),
60
+ model: llm.model ?? null,
61
+ baseUrl: llm.baseUrl ?? null,
62
+ apiKeyEnv: llm.apiKeyEnv ?? null,
63
+ ...(typeof llm.apiKey === "string" ? { apiKey: llm.apiKey } : {}),
64
+ },
65
+ };
66
+ }
67
+ /** The file's settings, or null when there's no file yet. Throws when it exists but is invalid. */
68
+ export async function readMonitorConfig() {
69
+ let file;
70
+ try {
71
+ file = await open(monitorConfigPath(), constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
72
+ }
73
+ catch (error) {
74
+ if (error.code === "ENOENT")
75
+ return null;
76
+ throw invalid("it can't be read");
77
+ }
78
+ try {
79
+ const stats = await file.stat();
80
+ if (!stats.isFile() || stats.size > 64 * 1024)
81
+ throw invalid("not a small regular file");
82
+ let parsed;
83
+ try {
84
+ parsed = JSON.parse(await file.readFile("utf8"));
85
+ }
86
+ catch {
87
+ throw invalid("not valid JSON");
88
+ }
89
+ return validateMonitorConfig(parsed);
90
+ }
91
+ finally {
92
+ await file.close();
93
+ }
94
+ }
95
+ export async function writeMonitorConfig(config) {
96
+ const safe = validateMonitorConfig(config);
97
+ await mkdir(getBeamHome(), { recursive: true, mode: 0o700 });
98
+ const tmp = `${monitorConfigPath()}.${randomUUID()}.tmp`;
99
+ try {
100
+ const file = await open(tmp, "wx", 0o600);
101
+ try {
102
+ await file.writeFile(`${JSON.stringify(safe, null, 2)}\n`);
103
+ }
104
+ finally {
105
+ await file.close();
106
+ }
107
+ await rename(tmp, monitorConfigPath());
108
+ }
109
+ finally {
110
+ await rm(tmp, { force: true });
111
+ }
112
+ }
113
+ /** What an upgrade carries over from older BEAM_LLM_* settings: names and endpoints, never a key. */
114
+ export function migratedFromEnv(env) {
115
+ const baseUrl = env.BEAM_LLM_BASE_URL?.trim() || null;
116
+ const model = env.BEAM_LLM_MODEL?.trim() || null;
117
+ const hasKey = Boolean(env.BEAM_LLM_API_KEY?.trim());
118
+ const provider = env.BEAM_LLM_PROVIDER?.trim().toLowerCase()
119
+ || (baseUrl && !/^https:\/\/api\.openai\.com\/v1\/?$/.test(baseUrl) ? "custom" : baseUrl || model || hasKey ? "openai" : "beam");
120
+ return { ...DEFAULT_MONITOR_CONFIG, llm: { provider, model, baseUrl, apiKeyEnv: hasKey ? "BEAM_LLM_API_KEY" : null } };
121
+ }
122
+ /**
123
+ * The file's settings, creating the file first if this is the first monitor command since an
124
+ * upgrade. A home directory that can't be written (a locked-down service user) isn't an error:
125
+ * the defaults apply and nothing is created.
126
+ */
127
+ export async function ensureMonitorConfig(env = process.env) {
128
+ const existing = await readMonitorConfig();
129
+ if (existing)
130
+ return { config: existing, created: false };
131
+ const config = migratedFromEnv(env);
132
+ try {
133
+ await writeMonitorConfig(config);
134
+ return { config, created: true };
135
+ }
136
+ catch {
137
+ return { config, created: false };
138
+ }
139
+ }
140
+ /** The settings for display: a stored key is shown as present, never printed. */
141
+ export function describeMonitorConfig(config) {
142
+ const { apiKey, ...llm } = config.llm;
143
+ return { ...config, llm: { ...llm, ...(apiKey ? { apiKey: "(stored)" } : {}) } };
144
+ }
145
+ const SETTING_KEYS = ["files", "app", "maxCpuPercent", "llm"];
146
+ export function managedConfigPath(env = process.env) {
147
+ if (env.BEAM_MANAGED_CONFIG)
148
+ return resolve(env.BEAM_MANAGED_CONFIG);
149
+ if (process.platform === "darwin")
150
+ return "/Library/Application Support/Beam/monitor.json";
151
+ if (process.platform === "win32")
152
+ return join(env.ProgramData || "C:\\ProgramData", "Beam", "monitor.json");
153
+ return "/etc/beam/monitor.json";
154
+ }
155
+ export const PROJECT_CONFIG_FILE = "beam.monitor.json";
156
+ /**
157
+ * A managed or project file: any subset of the settings. Keys are refused in both (they're shared
158
+ * files); `locked` is only meaningful, and only allowed, in the managed file.
159
+ */
160
+ export function validateLayer(value, kind, path) {
161
+ const bad = (why) => new Error(`${path} is invalid (${why}).`);
162
+ if (!isRecord(value) || (value.version !== undefined && value.version !== 1))
163
+ throw bad("unsupported version");
164
+ if (isRecord(value.llm) && value.llm.apiKey !== undefined)
165
+ throw bad("keys don't belong in a shared file: name the variable that holds it with llm.apiKeyEnv");
166
+ const locked = value.locked ?? [];
167
+ if (!Array.isArray(locked) || !locked.every(k => SETTING_KEYS.includes(k)))
168
+ throw bad(`locked must list settings: ${SETTING_KEYS.join(", ")}`);
169
+ if (kind === "project" && locked.length)
170
+ throw bad("only the managed config can lock settings");
171
+ // Reuse the full validation for field types, then keep only what this file set.
172
+ const full = validateMonitorConfig({ ...value, version: 1, ...(isRecord(value.llm) ? {} : { llm: undefined }) });
173
+ const settings = {};
174
+ for (const key of SETTING_KEYS)
175
+ if (value[key] !== undefined)
176
+ settings[key] = full[key];
177
+ return { settings, locked: locked };
178
+ }
179
+ async function readJsonFile(path) {
180
+ let file;
181
+ try {
182
+ file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
183
+ }
184
+ catch (error) {
185
+ if (error.code === "ENOENT")
186
+ return null;
187
+ throw new Error(`${path} can't be read.`);
188
+ }
189
+ try {
190
+ const stats = await file.stat();
191
+ if (!stats.isFile() || stats.size > 64 * 1024)
192
+ throw new Error(`${path} is invalid (not a small regular file).`);
193
+ try {
194
+ return JSON.parse(await file.readFile("utf8"));
195
+ }
196
+ catch {
197
+ throw new Error(`${path} is invalid (not valid JSON).`);
198
+ }
199
+ }
200
+ finally {
201
+ await file.close();
202
+ }
203
+ }
204
+ /** The effective file settings across the managed, user and project layers. Throws on an invalid file. */
205
+ export async function resolveMonitorSettings(options = {}) {
206
+ const env = options.env ?? process.env;
207
+ const settings = { ...DEFAULT_MONITOR_CONFIG, llm: { ...DEFAULT_MONITOR_CONFIG.llm } };
208
+ const origins = Object.fromEntries(SETTING_KEYS.map(k => [k, "default"]));
209
+ const locked = new Set();
210
+ const sources = [];
211
+ const warnings = [];
212
+ const apply = (layer, part) => {
213
+ for (const key of SETTING_KEYS) {
214
+ if (part[key] === undefined || locked.has(key))
215
+ continue;
216
+ settings[key] = part[key];
217
+ origins[key] = layer;
218
+ }
219
+ };
220
+ const managedPath = managedConfigPath(env);
221
+ const managed = await readJsonFile(managedPath);
222
+ if (managed === null)
223
+ sources.push({ layer: "managed", path: managedPath, status: "not found" });
224
+ else {
225
+ const layer = validateLayer(managed, "managed", managedPath);
226
+ apply("managed", layer.settings);
227
+ for (const key of layer.locked)
228
+ if (layer.settings[key] !== undefined)
229
+ locked.add(key);
230
+ sources.push({ layer: "managed", path: managedPath, status: "used" });
231
+ }
232
+ const { config: user, created } = await ensureMonitorConfig(env);
233
+ const userSet = {};
234
+ for (const key of SETTING_KEYS)
235
+ if (JSON.stringify(user[key]) !== JSON.stringify(DEFAULT_MONITOR_CONFIG[key]))
236
+ userSet[key] = user[key];
237
+ apply("user", userSet);
238
+ sources.push({ layer: "user", path: monitorConfigPath(), status: created ? "used (created now)" : "used" });
239
+ if (user.llm.apiKey && process.platform !== "win32") {
240
+ try {
241
+ const { stat } = await import("node:fs/promises");
242
+ if (((await stat(monitorConfigPath())).mode & 0o077) !== 0)
243
+ warnings.push(`${monitorConfigPath()} stores a key but other users can read it. Run: chmod 600 ${monitorConfigPath()}`);
244
+ }
245
+ catch { /* not created */ }
246
+ }
247
+ const projectPath = options.configPath ? resolve(options.configPath) : join(options.cwd ?? process.cwd(), PROJECT_CONFIG_FILE);
248
+ const project = await readJsonFile(projectPath);
249
+ if (project === null) {
250
+ if (options.configPath)
251
+ throw new Error(`${projectPath} doesn't exist.`);
252
+ sources.push({ layer: "project", path: projectPath, status: "not found" });
253
+ }
254
+ else {
255
+ apply("project", validateLayer(project, "project", projectPath).settings);
256
+ sources.push({ layer: "project", path: projectPath, status: "used" });
257
+ }
258
+ for (const key of locked)
259
+ if (origins[key] !== "managed")
260
+ origins[key] = "managed";
261
+ return { settings, origins, locked, sources, created, warnings };
262
+ }
263
+ //# sourceMappingURL=monitor-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monitor-config.js","sourceRoot":"","sources":["../src/monitor-config.ts"],"names":[],"mappings":"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"}
@@ -1,20 +1,65 @@
1
1
  import type { MonitorFinding, MonitorLevel, MonitorSeverity } from "./monitor-client.js";
2
+ import type { MonitorLlmSettings } from "./monitor-config.js";
3
+ export type LlmProvider = "openai" | "anthropic" | "gemini" | "groq" | "openrouter" | "mistral" | "deepseek" | "xai" | "ollama" | "custom";
2
4
  export interface OwnLlmConfig {
3
5
  baseUrl: string;
4
6
  model: string;
5
7
  apiKey?: string;
8
+ provider?: LlmProvider;
9
+ label?: string;
10
+ /** Where the key came from (a variable name, never the key), for status output. */
11
+ keySource?: string;
12
+ /** "anthropic" uses the Messages API through Anthropic's SDK; everything else is OpenAI-compatible chat completions. */
13
+ api?: "openai" | "anthropic";
14
+ /** Ask for JSON mode (response_format). Off for endpoints that reject it; the prompt still asks for JSON. */
15
+ json?: boolean;
16
+ /** Per-request timeout. Default 30 s; the in-app SDK uses less. */
17
+ timeoutMs?: number;
6
18
  }
7
19
  export type LlmVerdict = NonNullable<MonitorFinding["llm"]>;
8
- export declare const DEFAULT_LLM_BASE_URL = "https://api.openai.com/v1";
9
- export declare const DEFAULT_LLM_MODEL = "gpt-4.1-nano";
20
+ interface Preset {
21
+ label: string;
22
+ baseUrl: string;
23
+ keyEnv: string[];
24
+ model?: string;
25
+ api: "openai" | "anthropic";
26
+ json: boolean;
27
+ needsKey: boolean;
28
+ }
29
+ export declare const LLM_PROVIDERS: Record<LlmProvider, Preset>;
30
+ export declare const DEFAULT_LLM_BASE_URL: string;
31
+ export declare const DEFAULT_LLM_MODEL: string;
10
32
  export declare const LLM_CHUNK_SIZE = 10;
11
- /** Null when no own LLM is configured; throws on a half-finished configuration. */
12
- export declare function ownLlmConfig(env?: NodeJS.ProcessEnv): OwnLlmConfig | null;
33
+ /** Providers whose usual key variable is set here, to suggest --llm without using any of them. */
34
+ export declare function detectedLlmKeys(env?: NodeJS.ProcessEnv): {
35
+ provider: LlmProvider;
36
+ variable: string;
37
+ }[];
38
+ /**
39
+ * The server's own LLM, or null to leave LLM checks to the workspace (Beam's model, if an admin
40
+ * turned it on). Each setting comes from --llm (`choice`), then BEAM_LLM_* variables, then
41
+ * monitor.json (`file`). With no provider anywhere, the older BEAM_LLM_BASE_URL/MODEL/API_KEY
42
+ * settings still work on their own. Throws on a half-finished configuration.
43
+ */
44
+ export declare function ownLlmConfig(env?: NodeJS.ProcessEnv, choice?: string, file?: MonitorLlmSettings | null): OwnLlmConfig | null;
45
+ /** One line for logs and `beam monitor status`. Names where the key came from, never the key. */
46
+ export declare function describeOwnLlm(config: OwnLlmConfig): string;
13
47
  /** Same policy as the workspace's model: none on light, high-severity only on standard, every alert on strict. */
14
48
  export declare function wantsLlmCheck(severity: MonitorSeverity, level: MonitorLevel): boolean;
15
- export declare function parseVerdicts(content: string | null | undefined, count: number, model: string): LlmVerdict[];
49
+ /** Model text bound for dashboards and chat webhooks: no links, mentions or markup, one short line. */
50
+ export declare function cleanReason(reason: unknown): string;
51
+ /**
52
+ * One verdict per line. A line the answer doesn't cover, or covers unreadably, is "declined". When
53
+ * an answer repeats a line, the most cautious verdict wins. With `check`, a "benign" verdict
54
+ * counts only if the answer echoes it: a reply steered by an injected line usually doesn't.
55
+ */
56
+ export declare function parseVerdicts(content: string | null | undefined, count: number, model: string, check?: string): LlmVerdict[];
57
+ /** Not sent to a model: a line that tries to instruct one can't get a trustworthy verdict from one. */
58
+ export declare function skipsLlmCheck(f: Pick<MonitorFinding, "category" | "evidence">): boolean;
16
59
  /**
17
- * Attaches `llm` to each finding the level wants checked and that doesn't have a verdict yet.
18
- * Mutates the findings in place, so a batch kept for a retry isn't judged twice.
60
+ * Attaches `llm` to each finding the level wants checked and that doesn't have a verdict yet, and
61
+ * returns how many went to the model. Mutates the findings in place, so a batch kept for a retry
62
+ * isn't judged twice.
19
63
  */
20
64
  export declare function judgeWithOwnLlm(findings: MonitorFinding[], level: MonitorLevel, config: OwnLlmConfig): Promise<number>;
65
+ export {};