bashbros 0.1.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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +453 -0
  3. package/dist/audit-MCFNGOIM.js +11 -0
  4. package/dist/audit-MCFNGOIM.js.map +1 -0
  5. package/dist/chunk-43W3RVEL.js +2910 -0
  6. package/dist/chunk-43W3RVEL.js.map +1 -0
  7. package/dist/chunk-4R4GV5V2.js +213 -0
  8. package/dist/chunk-4R4GV5V2.js.map +1 -0
  9. package/dist/chunk-7OCVIDC7.js +12 -0
  10. package/dist/chunk-7OCVIDC7.js.map +1 -0
  11. package/dist/chunk-CSRPOGHY.js +354 -0
  12. package/dist/chunk-CSRPOGHY.js.map +1 -0
  13. package/dist/chunk-DEAF6PYM.js +212 -0
  14. package/dist/chunk-DEAF6PYM.js.map +1 -0
  15. package/dist/chunk-DLP2O6PN.js +273 -0
  16. package/dist/chunk-DLP2O6PN.js.map +1 -0
  17. package/dist/chunk-GD5VNHIN.js +519 -0
  18. package/dist/chunk-GD5VNHIN.js.map +1 -0
  19. package/dist/chunk-ID2O2QTI.js +269 -0
  20. package/dist/chunk-ID2O2QTI.js.map +1 -0
  21. package/dist/chunk-J37RHCFJ.js +357 -0
  22. package/dist/chunk-J37RHCFJ.js.map +1 -0
  23. package/dist/chunk-SB4JS3GU.js +456 -0
  24. package/dist/chunk-SB4JS3GU.js.map +1 -0
  25. package/dist/chunk-SG752FZC.js +200 -0
  26. package/dist/chunk-SG752FZC.js.map +1 -0
  27. package/dist/cli.d.ts +2 -0
  28. package/dist/cli.js +2448 -0
  29. package/dist/cli.js.map +1 -0
  30. package/dist/config-CZMIGNPF.js +13 -0
  31. package/dist/config-CZMIGNPF.js.map +1 -0
  32. package/dist/config-parser-XHE7BC7H.js +13 -0
  33. package/dist/config-parser-XHE7BC7H.js.map +1 -0
  34. package/dist/db-EHQDB5OL.js +11 -0
  35. package/dist/db-EHQDB5OL.js.map +1 -0
  36. package/dist/display-IN4NRJJS.js +18 -0
  37. package/dist/display-IN4NRJJS.js.map +1 -0
  38. package/dist/engine-PKLXW6OF.js +9 -0
  39. package/dist/engine-PKLXW6OF.js.map +1 -0
  40. package/dist/index.d.ts +1498 -0
  41. package/dist/index.js +552 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/moltbot-DXZFVK3X.js +11 -0
  44. package/dist/moltbot-DXZFVK3X.js.map +1 -0
  45. package/dist/ollama-HY35OHW4.js +9 -0
  46. package/dist/ollama-HY35OHW4.js.map +1 -0
  47. package/dist/risk-scorer-Y6KF2XCZ.js +9 -0
  48. package/dist/risk-scorer-Y6KF2XCZ.js.map +1 -0
  49. package/dist/static/index.html +410 -0
  50. package/package.json +68 -0
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ loadConfig
4
+ } from "./chunk-SB4JS3GU.js";
5
+
6
+ // src/transparency/display.ts
7
+ import chalk from "chalk";
8
+ var AGENT_DISPLAY_NAMES = {
9
+ "claude-code": "Claude Code",
10
+ "moltbot": "Moltbot",
11
+ "clawdbot": "Clawdbot (legacy)",
12
+ "aider": "Aider",
13
+ "gemini-cli": "Gemini CLI",
14
+ "opencode": "OpenCode",
15
+ "custom": "Custom Agent"
16
+ };
17
+ function formatAgentInfo(info) {
18
+ const lines = [];
19
+ const name = AGENT_DISPLAY_NAMES[info.agent] || info.agent;
20
+ lines.push(chalk.bold(`${name} (${info.agent})`));
21
+ const statusIcon = info.installed ? chalk.green("Installed") : chalk.dim("Not found");
22
+ lines.push(` Status: ${statusIcon}`);
23
+ if (info.version) {
24
+ lines.push(` Version: ${info.version}`);
25
+ }
26
+ if (info.configPath) {
27
+ const configStatus = info.configExists ? chalk.green(info.configPath) : chalk.yellow(`${info.configPath} (not found)`);
28
+ lines.push(` Config: ${configStatus}`);
29
+ } else {
30
+ lines.push(` Config: ${chalk.dim("No known config location")}`);
31
+ }
32
+ if (info.lastModified) {
33
+ lines.push(` Modified: ${info.lastModified.toLocaleDateString()} ${info.lastModified.toLocaleTimeString()}`);
34
+ }
35
+ if (info.hooks && info.hooks.length > 0) {
36
+ lines.push(` Hooks: ${info.hooks.join(", ")}`);
37
+ }
38
+ if (info.permissions) {
39
+ lines.push(` Permissions:`);
40
+ if (info.permissions.allowedPaths && info.permissions.allowedPaths.length > 0) {
41
+ const paths = info.permissions.allowedPaths.slice(0, 5).join(", ");
42
+ const more = info.permissions.allowedPaths.length > 5 ? ` (+${info.permissions.allowedPaths.length - 5} more)` : "";
43
+ lines.push(` Allowed paths: ${paths}${more}`);
44
+ }
45
+ if (info.permissions.blockedCommands && info.permissions.blockedCommands.length > 0) {
46
+ lines.push(` Blocked commands: ${info.permissions.blockedCommands.length} patterns`);
47
+ }
48
+ if (info.permissions.rateLimit) {
49
+ lines.push(` Rate limit: ${info.permissions.rateLimit}`);
50
+ }
51
+ if (info.permissions.securityProfile) {
52
+ lines.push(` Security profile: ${info.permissions.securityProfile}`);
53
+ }
54
+ }
55
+ const integrationStatus = info.bashbrosIntegrated ? chalk.green("Hooks installed") : chalk.yellow("Not integrated (no hooks)");
56
+ lines.push(` Bashbros: ${integrationStatus}`);
57
+ return lines.join("\n");
58
+ }
59
+ function formatAllAgentsInfo(agents) {
60
+ const lines = [
61
+ chalk.bold.cyan("AGENT CONFIGURATIONS"),
62
+ chalk.dim("=".repeat(40)),
63
+ ""
64
+ ];
65
+ const installed = agents.filter((a) => a.installed);
66
+ const notInstalled = agents.filter((a) => !a.installed);
67
+ if (installed.length === 0) {
68
+ lines.push(chalk.yellow("No agents detected."));
69
+ lines.push("");
70
+ } else {
71
+ for (const agent of installed) {
72
+ lines.push(formatAgentInfo(agent));
73
+ lines.push("");
74
+ }
75
+ }
76
+ if (notInstalled.length > 0) {
77
+ lines.push(chalk.dim("Other known agents (not installed):"));
78
+ lines.push(chalk.dim(" " + notInstalled.map((a) => AGENT_DISPLAY_NAMES[a.agent]).join(", ")));
79
+ }
80
+ return lines.join("\n");
81
+ }
82
+ function getEffectivePermissions(agentInfo) {
83
+ const bashbrosConfig = loadConfig();
84
+ const bashbrosPaths = bashbrosConfig.paths.allow;
85
+ const bashbrosBlocked = bashbrosConfig.commands.block;
86
+ const bashbrosRiskThreshold = bashbrosConfig.riskScoring.blockThreshold;
87
+ const bashbrosRateLimit = bashbrosConfig.rateLimit.maxPerMinute;
88
+ const agentPaths = agentInfo.permissions?.allowedPaths || [];
89
+ const agentBlocked = agentInfo.permissions?.blockedCommands || [];
90
+ const agentRiskThreshold = typeof agentInfo.permissions?.rateLimit === "number" ? agentInfo.permissions.rateLimit : null;
91
+ const agentRateLimit = null;
92
+ let effectivePaths;
93
+ if (bashbrosPaths.length > 0 && agentPaths.length > 0) {
94
+ effectivePaths = bashbrosPaths.filter(
95
+ (bp) => agentPaths.some((ap) => ap === bp || bp.startsWith(ap) || ap.startsWith(bp))
96
+ );
97
+ if (effectivePaths.length === 0) {
98
+ effectivePaths = bashbrosPaths;
99
+ }
100
+ } else {
101
+ effectivePaths = bashbrosPaths.length > 0 ? bashbrosPaths : agentPaths;
102
+ }
103
+ const effectiveBlocked = Array.from(/* @__PURE__ */ new Set([...bashbrosBlocked, ...agentBlocked]));
104
+ const effectiveRiskThreshold = agentRiskThreshold !== null ? Math.min(bashbrosRiskThreshold, agentRiskThreshold) : bashbrosRiskThreshold;
105
+ const effectiveRateLimit = agentRateLimit !== null ? Math.min(bashbrosRateLimit, agentRateLimit) : bashbrosRateLimit;
106
+ return {
107
+ allowedPaths: {
108
+ bashbros: bashbrosPaths,
109
+ agent: agentPaths,
110
+ effective: effectivePaths
111
+ },
112
+ riskThreshold: {
113
+ bashbros: bashbrosRiskThreshold,
114
+ agent: agentRiskThreshold,
115
+ effective: effectiveRiskThreshold
116
+ },
117
+ rateLimit: {
118
+ bashbros: bashbrosRateLimit,
119
+ agent: agentRateLimit,
120
+ effective: effectiveRateLimit
121
+ },
122
+ blockedCommands: {
123
+ bashbros: bashbrosBlocked,
124
+ agent: agentBlocked,
125
+ effective: effectiveBlocked
126
+ }
127
+ };
128
+ }
129
+ function formatPermissionsTable(agents) {
130
+ const lines = [
131
+ chalk.bold.cyan("EFFECTIVE PERMISSIONS"),
132
+ chalk.dim("=".repeat(40)),
133
+ ""
134
+ ];
135
+ const config = loadConfig();
136
+ const installedAgents = agents.filter((a) => a.installed);
137
+ const agentHeaders = installedAgents.map((a) => AGENT_DISPLAY_NAMES[a.agent].padEnd(12));
138
+ lines.push(chalk.bold(" Bashbros " + agentHeaders.join(" ") + " Effective"));
139
+ lines.push(chalk.dim("-".repeat(80)));
140
+ const bashbrosPaths = config.paths.allow.slice(0, 3).join(", ") || "*";
141
+ lines.push(`Allowed paths: ${bashbrosPaths.padEnd(12)}`);
142
+ for (const agent of installedAgents) {
143
+ const perms = getEffectivePermissions(agent);
144
+ const agentPaths = perms.allowedPaths.agent.slice(0, 2).join(", ") || "none";
145
+ const effectivePaths = perms.allowedPaths.effective.slice(0, 2).join(", ") || "*";
146
+ lines[lines.length - 1] += `${agentPaths.padEnd(12)} `;
147
+ if (agent === installedAgents[installedAgents.length - 1]) {
148
+ lines[lines.length - 1] += effectivePaths;
149
+ }
150
+ }
151
+ const bashbrosRisk = config.riskScoring.blockThreshold;
152
+ let riskLine = `Risk threshold: ${bashbrosRisk.toString().padEnd(12)}`;
153
+ for (const agent of installedAgents) {
154
+ const perms = getEffectivePermissions(agent);
155
+ const agentRisk = perms.riskThreshold.agent !== null ? perms.riskThreshold.agent.toString() : "none";
156
+ riskLine += `${agentRisk.padEnd(12)} `;
157
+ if (agent === installedAgents[installedAgents.length - 1]) {
158
+ const effectiveRisk = perms.riskThreshold.effective;
159
+ const note = effectiveRisk < bashbrosRisk ? " (stricter)" : "";
160
+ riskLine += `${effectiveRisk}${note}`;
161
+ }
162
+ }
163
+ lines.push(riskLine);
164
+ const bashbrosRate = `${config.rateLimit.maxPerMinute}/min`;
165
+ let rateLine = `Rate limit: ${bashbrosRate.padEnd(12)}`;
166
+ for (const agent of installedAgents) {
167
+ const perms = getEffectivePermissions(agent);
168
+ const agentRate = perms.rateLimit.agent !== null ? `${perms.rateLimit.agent}/min` : "none";
169
+ rateLine += `${agentRate.padEnd(12)} `;
170
+ if (agent === installedAgents[installedAgents.length - 1]) {
171
+ rateLine += `${perms.rateLimit.effective}/min`;
172
+ }
173
+ }
174
+ lines.push(rateLine);
175
+ const bashbrosBlocked = config.commands.block.length;
176
+ let blockedLine = `Blocked commands: ${bashbrosBlocked.toString().padEnd(12)}`;
177
+ for (const agent of installedAgents) {
178
+ const perms = getEffectivePermissions(agent);
179
+ const agentBlocked = perms.blockedCommands.agent.length;
180
+ blockedLine += `${agentBlocked.toString().padEnd(12)} `;
181
+ if (agent === installedAgents[installedAgents.length - 1]) {
182
+ blockedLine += perms.blockedCommands.effective.length.toString();
183
+ }
184
+ }
185
+ lines.push(blockedLine);
186
+ lines.push("");
187
+ lines.push(chalk.dim("Note: Effective = most restrictive combination of all policies"));
188
+ return lines.join("\n");
189
+ }
190
+ function formatAgentSummary(agents) {
191
+ const lines = [];
192
+ const installed = agents.filter((a) => a.installed);
193
+ if (installed.length === 0) {
194
+ lines.push(chalk.dim(" No agents detected"));
195
+ return lines.join("\n");
196
+ }
197
+ for (const agent of installed) {
198
+ const name = AGENT_DISPLAY_NAMES[agent.agent];
199
+ const config = agent.configExists ? chalk.green("configured") : chalk.yellow("no config");
200
+ const integration = agent.bashbrosIntegrated ? chalk.green("integrated") : chalk.dim("not integrated");
201
+ lines.push(` ${name}: ${config}, ${integration}`);
202
+ }
203
+ return lines.join("\n");
204
+ }
205
+
206
+ export {
207
+ formatAgentInfo,
208
+ formatAllAgentsInfo,
209
+ getEffectivePermissions,
210
+ formatPermissionsTable,
211
+ formatAgentSummary
212
+ };
213
+ //# sourceMappingURL=chunk-4R4GV5V2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/transparency/display.ts"],"sourcesContent":["/**\r\n * Display utilities for agent transparency\r\n * Formats agent info and permissions for CLI output\r\n */\r\n\r\nimport chalk from 'chalk'\r\nimport type { AgentConfigInfo, EffectivePermissions, AgentType } from '../types.js'\r\nimport { loadConfig } from '../config.js'\r\n\r\n// Human-readable agent names\r\nconst AGENT_DISPLAY_NAMES: Record<AgentType, string> = {\r\n 'claude-code': 'Claude Code',\r\n 'moltbot': 'Moltbot',\r\n 'clawdbot': 'Clawdbot (legacy)',\r\n 'aider': 'Aider',\r\n 'gemini-cli': 'Gemini CLI',\r\n 'opencode': 'OpenCode',\r\n 'custom': 'Custom Agent'\r\n}\r\n\r\n/**\r\n * Format a single agent's info for display\r\n */\r\nexport function formatAgentInfo(info: AgentConfigInfo): string {\r\n const lines: string[] = []\r\n const name = AGENT_DISPLAY_NAMES[info.agent] || info.agent\r\n\r\n lines.push(chalk.bold(`${name} (${info.agent})`))\r\n\r\n // Status\r\n const statusIcon = info.installed ? chalk.green('Installed') : chalk.dim('Not found')\r\n lines.push(` Status: ${statusIcon}`)\r\n\r\n if (info.version) {\r\n lines.push(` Version: ${info.version}`)\r\n }\r\n\r\n // Config\r\n if (info.configPath) {\r\n const configStatus = info.configExists ? chalk.green(info.configPath) : chalk.yellow(`${info.configPath} (not found)`)\r\n lines.push(` Config: ${configStatus}`)\r\n } else {\r\n lines.push(` Config: ${chalk.dim('No known config location')}`)\r\n }\r\n\r\n if (info.lastModified) {\r\n lines.push(` Modified: ${info.lastModified.toLocaleDateString()} ${info.lastModified.toLocaleTimeString()}`)\r\n }\r\n\r\n // Hooks\r\n if (info.hooks && info.hooks.length > 0) {\r\n lines.push(` Hooks: ${info.hooks.join(', ')}`)\r\n }\r\n\r\n // Permissions\r\n if (info.permissions) {\r\n lines.push(` Permissions:`)\r\n\r\n if (info.permissions.allowedPaths && info.permissions.allowedPaths.length > 0) {\r\n const paths = info.permissions.allowedPaths.slice(0, 5).join(', ')\r\n const more = info.permissions.allowedPaths.length > 5 ? ` (+${info.permissions.allowedPaths.length - 5} more)` : ''\r\n lines.push(` Allowed paths: ${paths}${more}`)\r\n }\r\n\r\n if (info.permissions.blockedCommands && info.permissions.blockedCommands.length > 0) {\r\n lines.push(` Blocked commands: ${info.permissions.blockedCommands.length} patterns`)\r\n }\r\n\r\n if (info.permissions.rateLimit) {\r\n lines.push(` Rate limit: ${info.permissions.rateLimit}`)\r\n }\r\n\r\n if (info.permissions.securityProfile) {\r\n lines.push(` Security profile: ${info.permissions.securityProfile}`)\r\n }\r\n }\r\n\r\n // BashBros integration\r\n const integrationStatus = info.bashbrosIntegrated\r\n ? chalk.green('Hooks installed')\r\n : chalk.yellow('Not integrated (no hooks)')\r\n lines.push(` Bashbros: ${integrationStatus}`)\r\n\r\n return lines.join('\\n')\r\n}\r\n\r\n/**\r\n * Format all agents info\r\n */\r\nexport function formatAllAgentsInfo(agents: AgentConfigInfo[]): string {\r\n const lines: string[] = [\r\n chalk.bold.cyan('AGENT CONFIGURATIONS'),\r\n chalk.dim('='.repeat(40)),\r\n ''\r\n ]\r\n\r\n const installed = agents.filter(a => a.installed)\r\n const notInstalled = agents.filter(a => !a.installed)\r\n\r\n if (installed.length === 0) {\r\n lines.push(chalk.yellow('No agents detected.'))\r\n lines.push('')\r\n } else {\r\n for (const agent of installed) {\r\n lines.push(formatAgentInfo(agent))\r\n lines.push('')\r\n }\r\n }\r\n\r\n // Show not-installed agents briefly\r\n if (notInstalled.length > 0) {\r\n lines.push(chalk.dim('Other known agents (not installed):'))\r\n lines.push(chalk.dim(' ' + notInstalled.map(a => AGENT_DISPLAY_NAMES[a.agent]).join(', ')))\r\n }\r\n\r\n return lines.join('\\n')\r\n}\r\n\r\n/**\r\n * Calculate effective permissions by combining bashbros config with agent config\r\n */\r\nexport function getEffectivePermissions(agentInfo: AgentConfigInfo): EffectivePermissions {\r\n const bashbrosConfig = loadConfig()\r\n\r\n // Get bashbros settings\r\n const bashbrosPaths = bashbrosConfig.paths.allow\r\n const bashbrosBlocked = bashbrosConfig.commands.block\r\n const bashbrosRiskThreshold = bashbrosConfig.riskScoring.blockThreshold\r\n const bashbrosRateLimit = bashbrosConfig.rateLimit.maxPerMinute\r\n\r\n // Get agent settings\r\n const agentPaths = agentInfo.permissions?.allowedPaths || []\r\n const agentBlocked = agentInfo.permissions?.blockedCommands || []\r\n const agentRiskThreshold = typeof agentInfo.permissions?.rateLimit === 'number'\r\n ? agentInfo.permissions.rateLimit\r\n : null\r\n const agentRateLimit = null // Most agents don't have rate limits in their config\r\n\r\n // Calculate effective (most restrictive)\r\n // For paths: intersection if both have values, otherwise the one that exists\r\n let effectivePaths: string[]\r\n if (bashbrosPaths.length > 0 && agentPaths.length > 0) {\r\n // Find paths that appear in both (simplified intersection)\r\n effectivePaths = bashbrosPaths.filter(bp =>\r\n agentPaths.some(ap => ap === bp || bp.startsWith(ap) || ap.startsWith(bp))\r\n )\r\n if (effectivePaths.length === 0) {\r\n effectivePaths = bashbrosPaths // Fall back to bashbros if no overlap\r\n }\r\n } else {\r\n effectivePaths = bashbrosPaths.length > 0 ? bashbrosPaths : agentPaths\r\n }\r\n\r\n // For blocked commands: union\r\n const effectiveBlocked = Array.from(new Set([...bashbrosBlocked, ...agentBlocked]))\r\n\r\n // For numeric thresholds: use the more restrictive (lower) value\r\n const effectiveRiskThreshold = agentRiskThreshold !== null\r\n ? Math.min(bashbrosRiskThreshold, agentRiskThreshold)\r\n : bashbrosRiskThreshold\r\n\r\n const effectiveRateLimit = agentRateLimit !== null\r\n ? Math.min(bashbrosRateLimit, agentRateLimit)\r\n : bashbrosRateLimit\r\n\r\n return {\r\n allowedPaths: {\r\n bashbros: bashbrosPaths,\r\n agent: agentPaths,\r\n effective: effectivePaths\r\n },\r\n riskThreshold: {\r\n bashbros: bashbrosRiskThreshold,\r\n agent: agentRiskThreshold,\r\n effective: effectiveRiskThreshold\r\n },\r\n rateLimit: {\r\n bashbros: bashbrosRateLimit,\r\n agent: agentRateLimit,\r\n effective: effectiveRateLimit\r\n },\r\n blockedCommands: {\r\n bashbros: bashbrosBlocked,\r\n agent: agentBlocked,\r\n effective: effectiveBlocked\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Format permissions comparison table\r\n */\r\nexport function formatPermissionsTable(\r\n agents: AgentConfigInfo[]\r\n): string {\r\n const lines: string[] = [\r\n chalk.bold.cyan('EFFECTIVE PERMISSIONS'),\r\n chalk.dim('='.repeat(40)),\r\n ''\r\n ]\r\n\r\n const config = loadConfig()\r\n\r\n // Header row\r\n const installedAgents = agents.filter(a => a.installed)\r\n const agentHeaders = installedAgents.map(a => AGENT_DISPLAY_NAMES[a.agent].padEnd(12))\r\n\r\n lines.push(chalk.bold(' Bashbros ' + agentHeaders.join(' ') + ' Effective'))\r\n lines.push(chalk.dim('-'.repeat(80)))\r\n\r\n // Allowed paths\r\n const bashbrosPaths = config.paths.allow.slice(0, 3).join(', ') || '*'\r\n lines.push(`Allowed paths: ${bashbrosPaths.padEnd(12)}`)\r\n\r\n for (const agent of installedAgents) {\r\n const perms = getEffectivePermissions(agent)\r\n const agentPaths = perms.allowedPaths.agent.slice(0, 2).join(', ') || 'none'\r\n const effectivePaths = perms.allowedPaths.effective.slice(0, 2).join(', ') || '*'\r\n\r\n lines[lines.length - 1] += `${agentPaths.padEnd(12)} `\r\n\r\n if (agent === installedAgents[installedAgents.length - 1]) {\r\n lines[lines.length - 1] += effectivePaths\r\n }\r\n }\r\n\r\n // Risk threshold\r\n const bashbrosRisk = config.riskScoring.blockThreshold\r\n let riskLine = `Risk threshold: ${bashbrosRisk.toString().padEnd(12)}`\r\n\r\n for (const agent of installedAgents) {\r\n const perms = getEffectivePermissions(agent)\r\n const agentRisk = perms.riskThreshold.agent !== null ? perms.riskThreshold.agent.toString() : 'none'\r\n riskLine += `${agentRisk.padEnd(12)} `\r\n\r\n if (agent === installedAgents[installedAgents.length - 1]) {\r\n const effectiveRisk = perms.riskThreshold.effective\r\n const note = effectiveRisk < bashbrosRisk ? ' (stricter)' : ''\r\n riskLine += `${effectiveRisk}${note}`\r\n }\r\n }\r\n lines.push(riskLine)\r\n\r\n // Rate limit\r\n const bashbrosRate = `${config.rateLimit.maxPerMinute}/min`\r\n let rateLine = `Rate limit: ${bashbrosRate.padEnd(12)}`\r\n\r\n for (const agent of installedAgents) {\r\n const perms = getEffectivePermissions(agent)\r\n const agentRate = perms.rateLimit.agent !== null ? `${perms.rateLimit.agent}/min` : 'none'\r\n rateLine += `${agentRate.padEnd(12)} `\r\n\r\n if (agent === installedAgents[installedAgents.length - 1]) {\r\n rateLine += `${perms.rateLimit.effective}/min`\r\n }\r\n }\r\n lines.push(rateLine)\r\n\r\n // Blocked commands count\r\n const bashbrosBlocked = config.commands.block.length\r\n let blockedLine = `Blocked commands: ${bashbrosBlocked.toString().padEnd(12)}`\r\n\r\n for (const agent of installedAgents) {\r\n const perms = getEffectivePermissions(agent)\r\n const agentBlocked = perms.blockedCommands.agent.length\r\n blockedLine += `${agentBlocked.toString().padEnd(12)} `\r\n\r\n if (agent === installedAgents[installedAgents.length - 1]) {\r\n blockedLine += perms.blockedCommands.effective.length.toString()\r\n }\r\n }\r\n lines.push(blockedLine)\r\n\r\n lines.push('')\r\n lines.push(chalk.dim('Note: Effective = most restrictive combination of all policies'))\r\n\r\n return lines.join('\\n')\r\n}\r\n\r\n/**\r\n * Format a brief summary for scan output\r\n */\r\nexport function formatAgentSummary(agents: AgentConfigInfo[]): string {\r\n const lines: string[] = []\r\n const installed = agents.filter(a => a.installed)\r\n\r\n if (installed.length === 0) {\r\n lines.push(chalk.dim(' No agents detected'))\r\n return lines.join('\\n')\r\n }\r\n\r\n for (const agent of installed) {\r\n const name = AGENT_DISPLAY_NAMES[agent.agent]\r\n const config = agent.configExists\r\n ? chalk.green('configured')\r\n : chalk.yellow('no config')\r\n const integration = agent.bashbrosIntegrated\r\n ? chalk.green('integrated')\r\n : chalk.dim('not integrated')\r\n\r\n lines.push(` ${name}: ${config}, ${integration}`)\r\n }\r\n\r\n return lines.join('\\n')\r\n}\r\n"],"mappings":"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","names":[]}
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
+ }) : x)(function(x) {
5
+ if (typeof require !== "undefined") return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
8
+
9
+ export {
10
+ __require
11
+ };
12
+ //# sourceMappingURL=chunk-7OCVIDC7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,354 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/dashboard/db.ts
4
+ import Database from "better-sqlite3";
5
+ import { randomUUID } from "crypto";
6
+ var DashboardDB = class {
7
+ db;
8
+ constructor(dbPath = ".bashbros.db") {
9
+ this.db = new Database(dbPath);
10
+ this.db.pragma("journal_mode = WAL");
11
+ this.initTables();
12
+ }
13
+ initTables() {
14
+ this.db.exec(`
15
+ CREATE TABLE IF NOT EXISTS events (
16
+ id TEXT PRIMARY KEY,
17
+ timestamp TEXT NOT NULL,
18
+ source TEXT NOT NULL,
19
+ level TEXT NOT NULL,
20
+ category TEXT NOT NULL,
21
+ message TEXT NOT NULL,
22
+ data TEXT
23
+ )
24
+ `);
25
+ this.db.exec(`
26
+ CREATE TABLE IF NOT EXISTS connector_events (
27
+ id TEXT PRIMARY KEY,
28
+ timestamp TEXT NOT NULL,
29
+ connector TEXT NOT NULL,
30
+ method TEXT NOT NULL,
31
+ direction TEXT NOT NULL,
32
+ payload TEXT NOT NULL,
33
+ resources_accessed TEXT NOT NULL
34
+ )
35
+ `);
36
+ this.db.exec(`
37
+ CREATE TABLE IF NOT EXISTS egress_blocks (
38
+ id TEXT PRIMARY KEY,
39
+ timestamp TEXT NOT NULL,
40
+ pattern TEXT NOT NULL,
41
+ matched_text TEXT NOT NULL,
42
+ redacted_text TEXT NOT NULL,
43
+ connector TEXT,
44
+ destination TEXT,
45
+ status TEXT NOT NULL DEFAULT 'pending',
46
+ approved_by TEXT,
47
+ approved_at TEXT
48
+ )
49
+ `);
50
+ this.db.exec(`
51
+ CREATE TABLE IF NOT EXISTS exposure_scans (
52
+ id TEXT PRIMARY KEY,
53
+ timestamp TEXT NOT NULL,
54
+ agent TEXT NOT NULL,
55
+ pid INTEGER,
56
+ port INTEGER NOT NULL,
57
+ bind_address TEXT NOT NULL,
58
+ has_auth TEXT NOT NULL,
59
+ severity TEXT NOT NULL,
60
+ action TEXT NOT NULL,
61
+ message TEXT NOT NULL
62
+ )
63
+ `);
64
+ this.db.exec(`
65
+ CREATE INDEX IF NOT EXISTS idx_events_source ON events(source);
66
+ CREATE INDEX IF NOT EXISTS idx_events_level ON events(level);
67
+ CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
68
+ CREATE INDEX IF NOT EXISTS idx_connector_events_connector ON connector_events(connector);
69
+ CREATE INDEX IF NOT EXISTS idx_egress_blocks_status ON egress_blocks(status);
70
+ `);
71
+ }
72
+ // ─────────────────────────────────────────────────────────────
73
+ // Events
74
+ // ─────────────────────────────────────────────────────────────
75
+ insertEvent(input) {
76
+ const id = randomUUID();
77
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
78
+ const data = input.data ? JSON.stringify(input.data) : null;
79
+ const stmt = this.db.prepare(`
80
+ INSERT INTO events (id, timestamp, source, level, category, message, data)
81
+ VALUES (?, ?, ?, ?, ?, ?, ?)
82
+ `);
83
+ stmt.run(id, timestamp, input.source, input.level, input.category, input.message, data);
84
+ return id;
85
+ }
86
+ getEvents(filter = {}) {
87
+ const conditions = [];
88
+ const params = [];
89
+ if (filter.source) {
90
+ conditions.push("source = ?");
91
+ params.push(filter.source);
92
+ }
93
+ if (filter.level) {
94
+ conditions.push("level = ?");
95
+ params.push(filter.level);
96
+ }
97
+ if (filter.category) {
98
+ conditions.push("category = ?");
99
+ params.push(filter.category);
100
+ }
101
+ if (filter.since) {
102
+ conditions.push("timestamp >= ?");
103
+ params.push(filter.since.toISOString());
104
+ }
105
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
106
+ const limit = filter.limit ?? 100;
107
+ const offset = filter.offset ?? 0;
108
+ const stmt = this.db.prepare(`
109
+ SELECT * FROM events
110
+ ${whereClause}
111
+ ORDER BY timestamp DESC
112
+ LIMIT ? OFFSET ?
113
+ `);
114
+ params.push(limit, offset);
115
+ const rows = stmt.all(...params);
116
+ return rows.map((row) => ({
117
+ id: row.id,
118
+ timestamp: new Date(row.timestamp),
119
+ source: row.source,
120
+ level: row.level,
121
+ category: row.category,
122
+ message: row.message,
123
+ data: row.data ? JSON.parse(row.data) : void 0
124
+ }));
125
+ }
126
+ // ─────────────────────────────────────────────────────────────
127
+ // Connector Events
128
+ // ─────────────────────────────────────────────────────────────
129
+ insertConnectorEvent(input) {
130
+ const id = randomUUID();
131
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
132
+ const stmt = this.db.prepare(`
133
+ INSERT INTO connector_events (id, timestamp, connector, method, direction, payload, resources_accessed)
134
+ VALUES (?, ?, ?, ?, ?, ?, ?)
135
+ `);
136
+ stmt.run(
137
+ id,
138
+ timestamp,
139
+ input.connector,
140
+ input.method,
141
+ input.direction,
142
+ JSON.stringify(input.payload),
143
+ JSON.stringify(input.resourcesAccessed)
144
+ );
145
+ return id;
146
+ }
147
+ getConnectorEvents(connector, limit = 100) {
148
+ const stmt = this.db.prepare(`
149
+ SELECT * FROM connector_events
150
+ WHERE connector = ?
151
+ ORDER BY timestamp DESC
152
+ LIMIT ?
153
+ `);
154
+ const rows = stmt.all(connector, limit);
155
+ return rows.map((row) => ({
156
+ id: row.id,
157
+ timestamp: new Date(row.timestamp),
158
+ connector: row.connector,
159
+ method: row.method,
160
+ direction: row.direction,
161
+ payload: JSON.parse(row.payload),
162
+ resourcesAccessed: JSON.parse(row.resources_accessed)
163
+ }));
164
+ }
165
+ getAllConnectorEvents(limit = 100) {
166
+ const stmt = this.db.prepare(`
167
+ SELECT * FROM connector_events
168
+ ORDER BY timestamp DESC
169
+ LIMIT ?
170
+ `);
171
+ const rows = stmt.all(limit);
172
+ return rows.map((row) => ({
173
+ id: row.id,
174
+ timestamp: new Date(row.timestamp),
175
+ connector: row.connector,
176
+ method: row.method,
177
+ direction: row.direction,
178
+ payload: JSON.parse(row.payload),
179
+ resourcesAccessed: JSON.parse(row.resources_accessed)
180
+ }));
181
+ }
182
+ // ─────────────────────────────────────────────────────────────
183
+ // Egress Blocks
184
+ // ─────────────────────────────────────────────────────────────
185
+ insertEgressBlock(input) {
186
+ const id = randomUUID();
187
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
188
+ const stmt = this.db.prepare(`
189
+ INSERT INTO egress_blocks (id, timestamp, pattern, matched_text, redacted_text, connector, destination, status)
190
+ VALUES (?, ?, ?, ?, ?, ?, ?, 'pending')
191
+ `);
192
+ stmt.run(
193
+ id,
194
+ timestamp,
195
+ JSON.stringify(input.pattern),
196
+ input.matchedText,
197
+ input.redactedText,
198
+ input.connector ?? null,
199
+ input.destination ?? null
200
+ );
201
+ return id;
202
+ }
203
+ getPendingBlocks() {
204
+ const stmt = this.db.prepare(`
205
+ SELECT * FROM egress_blocks
206
+ WHERE status = 'pending'
207
+ ORDER BY timestamp DESC
208
+ `);
209
+ const rows = stmt.all();
210
+ return rows.map((row) => this.rowToEgressMatch(row));
211
+ }
212
+ getBlock(id) {
213
+ const stmt = this.db.prepare(`
214
+ SELECT * FROM egress_blocks WHERE id = ?
215
+ `);
216
+ const row = stmt.get(id);
217
+ if (!row) return null;
218
+ return this.rowToEgressMatch(row);
219
+ }
220
+ approveBlock(id, approvedBy) {
221
+ const stmt = this.db.prepare(`
222
+ UPDATE egress_blocks
223
+ SET status = 'approved', approved_by = ?, approved_at = ?
224
+ WHERE id = ?
225
+ `);
226
+ stmt.run(approvedBy, (/* @__PURE__ */ new Date()).toISOString(), id);
227
+ }
228
+ denyBlock(id, deniedBy) {
229
+ const stmt = this.db.prepare(`
230
+ UPDATE egress_blocks
231
+ SET status = 'denied', approved_by = ?, approved_at = ?
232
+ WHERE id = ?
233
+ `);
234
+ stmt.run(deniedBy, (/* @__PURE__ */ new Date()).toISOString(), id);
235
+ }
236
+ rowToEgressMatch(row) {
237
+ return {
238
+ id: row.id,
239
+ timestamp: new Date(row.timestamp),
240
+ pattern: JSON.parse(row.pattern),
241
+ matchedText: row.matched_text,
242
+ redactedText: row.redacted_text,
243
+ connector: row.connector ?? void 0,
244
+ destination: row.destination ?? void 0,
245
+ status: row.status,
246
+ approvedBy: row.approved_by ?? void 0,
247
+ approvedAt: row.approved_at ? new Date(row.approved_at) : void 0
248
+ };
249
+ }
250
+ // ─────────────────────────────────────────────────────────────
251
+ // Exposure Scans
252
+ // ─────────────────────────────────────────────────────────────
253
+ insertExposureScan(result) {
254
+ const id = randomUUID();
255
+ const timestamp = result.timestamp.toISOString();
256
+ const stmt = this.db.prepare(`
257
+ INSERT INTO exposure_scans (id, timestamp, agent, pid, port, bind_address, has_auth, severity, action, message)
258
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
259
+ `);
260
+ stmt.run(
261
+ id,
262
+ timestamp,
263
+ result.agent,
264
+ result.pid ?? null,
265
+ result.port,
266
+ result.bindAddress,
267
+ String(result.hasAuth),
268
+ result.severity,
269
+ result.action,
270
+ result.message
271
+ );
272
+ return id;
273
+ }
274
+ getRecentExposures(limit = 100) {
275
+ const stmt = this.db.prepare(`
276
+ SELECT * FROM exposure_scans
277
+ ORDER BY timestamp DESC
278
+ LIMIT ?
279
+ `);
280
+ const rows = stmt.all(limit);
281
+ return rows.map((row) => ({
282
+ agent: row.agent,
283
+ pid: row.pid ?? void 0,
284
+ port: row.port,
285
+ bindAddress: row.bind_address,
286
+ hasAuth: row.has_auth === "true" ? true : row.has_auth === "false" ? false : "unknown",
287
+ severity: row.severity,
288
+ action: row.action,
289
+ message: row.message,
290
+ timestamp: new Date(row.timestamp)
291
+ }));
292
+ }
293
+ // ─────────────────────────────────────────────────────────────
294
+ // Stats
295
+ // ─────────────────────────────────────────────────────────────
296
+ getStats() {
297
+ const totalEventsRow = this.db.prepare("SELECT COUNT(*) as count FROM events").get();
298
+ const sourceRows = this.db.prepare(`
299
+ SELECT source, COUNT(*) as count FROM events GROUP BY source
300
+ `).all();
301
+ const eventsBySource = {};
302
+ for (const row of sourceRows) {
303
+ eventsBySource[row.source] = row.count;
304
+ }
305
+ const levelRows = this.db.prepare(`
306
+ SELECT level, COUNT(*) as count FROM events GROUP BY level
307
+ `).all();
308
+ const eventsByLevel = {};
309
+ for (const row of levelRows) {
310
+ eventsByLevel[row.level] = row.count;
311
+ }
312
+ const pendingBlocksRow = this.db.prepare(`
313
+ SELECT COUNT(*) as count FROM egress_blocks WHERE status = 'pending'
314
+ `).get();
315
+ const connectorCountRow = this.db.prepare(`
316
+ SELECT COUNT(DISTINCT connector) as count FROM connector_events
317
+ `).get();
318
+ const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1e3).toISOString();
319
+ const recentExposuresRow = this.db.prepare(`
320
+ SELECT COUNT(*) as count FROM exposure_scans WHERE timestamp >= ?
321
+ `).get(oneDayAgo);
322
+ return {
323
+ totalEvents: totalEventsRow.count,
324
+ eventsBySource,
325
+ eventsByLevel,
326
+ pendingBlocks: pendingBlocksRow.count,
327
+ connectorCount: connectorCountRow.count,
328
+ recentExposures: recentExposuresRow.count
329
+ };
330
+ }
331
+ // ─────────────────────────────────────────────────────────────
332
+ // Maintenance
333
+ // ─────────────────────────────────────────────────────────────
334
+ cleanup(olderThanDays = 30) {
335
+ const cutoff = new Date(Date.now() - olderThanDays * 24 * 60 * 60 * 1e3).toISOString();
336
+ const eventsDeleted = this.db.prepare("DELETE FROM events WHERE timestamp < ?").run(cutoff).changes;
337
+ const connectorDeleted = this.db.prepare("DELETE FROM connector_events WHERE timestamp < ?").run(cutoff).changes;
338
+ const blocksDeleted = this.db.prepare(`
339
+ DELETE FROM egress_blocks WHERE timestamp < ? AND status != 'pending'
340
+ `).run(cutoff).changes;
341
+ const exposuresDeleted = this.db.prepare("DELETE FROM exposure_scans WHERE timestamp < ?").run(cutoff).changes;
342
+ return eventsDeleted + connectorDeleted + blocksDeleted + exposuresDeleted;
343
+ }
344
+ close() {
345
+ this.db.close();
346
+ }
347
+ };
348
+ var db_default = DashboardDB;
349
+
350
+ export {
351
+ DashboardDB,
352
+ db_default
353
+ };
354
+ //# sourceMappingURL=chunk-CSRPOGHY.js.map