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,269 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/transparency/config-parser.ts
4
+ import { readFileSync } from "fs";
5
+ import { parse as parseYaml } from "yaml";
6
+ var SENSITIVE_FIELD_PATTERNS = [
7
+ /^api[_-]?key$/i,
8
+ /^apikey$/i,
9
+ /^secret$/i,
10
+ /^password$/i,
11
+ /^credential[s]?$/i,
12
+ /^token$/i,
13
+ /^bearer$/i,
14
+ /^auth$/i,
15
+ /^private[_-]?key$/i,
16
+ /_key$/i,
17
+ /_token$/i,
18
+ /_secret$/i,
19
+ /_password$/i,
20
+ /^access[_-]?token$/i,
21
+ /^refresh[_-]?token$/i,
22
+ /^client[_-]?secret$/i,
23
+ /^encryption[_-]?key$/i
24
+ ];
25
+ var SENSITIVE_VALUE_PATTERNS = [
26
+ /^sk-[a-zA-Z0-9]{20,}$/,
27
+ // OpenAI API key
28
+ /^ghp_[a-zA-Z0-9]{36}$/,
29
+ // GitHub personal access token
30
+ /^gho_[a-zA-Z0-9]{36}$/,
31
+ // GitHub OAuth token
32
+ /^glpat-[a-zA-Z0-9\-]{20,}$/,
33
+ // GitLab personal access token
34
+ /^xoxb-[a-zA-Z0-9\-]+$/,
35
+ // Slack bot token
36
+ /^xoxp-[a-zA-Z0-9\-]+$/,
37
+ // Slack user token
38
+ /^AKIA[A-Z0-9]{16}$/,
39
+ // AWS access key
40
+ /^-----BEGIN.*PRIVATE KEY-----/,
41
+ // PEM private key
42
+ /^Bearer\s+[a-zA-Z0-9\-._~+\/]+=*$/i
43
+ // Bearer token
44
+ ];
45
+ var REDACTED_MARKER = "[REDACTED]";
46
+ function isSensitiveFieldName(name) {
47
+ return SENSITIVE_FIELD_PATTERNS.some((pattern) => pattern.test(name));
48
+ }
49
+ function isSensitiveValue(value) {
50
+ if (typeof value !== "string") return false;
51
+ return SENSITIVE_VALUE_PATTERNS.some((pattern) => pattern.test(value));
52
+ }
53
+ function redactSensitiveData(obj, path = "") {
54
+ if (obj === null || obj === void 0) {
55
+ return obj;
56
+ }
57
+ if (Array.isArray(obj)) {
58
+ return obj.map((item, index) => redactSensitiveData(item, `${path}[${index}]`));
59
+ }
60
+ if (typeof obj === "object") {
61
+ const result = {};
62
+ for (const [key, value] of Object.entries(obj)) {
63
+ if (isSensitiveFieldName(key)) {
64
+ result[key] = REDACTED_MARKER;
65
+ } else if (typeof value === "string" && isSensitiveValue(value)) {
66
+ result[key] = REDACTED_MARKER;
67
+ } else {
68
+ result[key] = redactSensitiveData(value, `${path}.${key}`);
69
+ }
70
+ }
71
+ return result;
72
+ }
73
+ if (typeof obj === "string" && isSensitiveValue(obj)) {
74
+ return REDACTED_MARKER;
75
+ }
76
+ return obj;
77
+ }
78
+ function parseClaudeCodeConfig(content) {
79
+ try {
80
+ const config = JSON.parse(content);
81
+ const result = {
82
+ rawRedacted: redactSensitiveData(config)
83
+ };
84
+ if (config.hooks) {
85
+ result.hooks = Object.keys(config.hooks);
86
+ }
87
+ if (config.security || config.permissions) {
88
+ result.permissions = {
89
+ customPolicies: redactSensitiveData(config.security || config.permissions)
90
+ };
91
+ }
92
+ return result;
93
+ } catch {
94
+ return {};
95
+ }
96
+ }
97
+ function parseClawdbotConfig(content) {
98
+ try {
99
+ const config = parseYaml(content, { strict: true });
100
+ const result = {
101
+ rawRedacted: redactSensitiveData(config)
102
+ };
103
+ if (config.security) {
104
+ result.permissions = {
105
+ allowedPaths: config.security.allowedPaths,
106
+ blockedCommands: config.security.blockedCommands,
107
+ securityProfile: config.security.profile,
108
+ customPolicies: redactSensitiveData(config.security)
109
+ };
110
+ if (config.security.riskThreshold) {
111
+ result.permissions.rateLimit = config.security.riskThreshold;
112
+ }
113
+ }
114
+ if (config.hooks) {
115
+ result.hooks = Object.keys(config.hooks);
116
+ }
117
+ return result;
118
+ } catch {
119
+ return {};
120
+ }
121
+ }
122
+ function parseMoltbotJsonConfig(content) {
123
+ try {
124
+ const config = JSON.parse(content);
125
+ const result = {
126
+ rawRedacted: redactSensitiveData(config)
127
+ };
128
+ const gateway = config.gateway || {};
129
+ const sandbox = config.agents?.defaults?.sandbox || {};
130
+ result.permissions = {
131
+ securityProfile: sandbox.mode,
132
+ customPolicies: {
133
+ gateway: redactSensitiveData(gateway),
134
+ sandbox: redactSensitiveData(sandbox)
135
+ }
136
+ };
137
+ if (config.security) {
138
+ result.permissions.allowedPaths = config.security.allowedPaths;
139
+ result.permissions.blockedCommands = config.security.blockedCommands;
140
+ }
141
+ if (config.hooks) {
142
+ result.hooks = Object.keys(config.hooks);
143
+ }
144
+ return result;
145
+ } catch {
146
+ return {};
147
+ }
148
+ }
149
+ function parseAiderConfig(content) {
150
+ try {
151
+ const config = parseYaml(content, { strict: true });
152
+ const result = {
153
+ rawRedacted: redactSensitiveData(config)
154
+ };
155
+ if (config.auto_commits === false || config.dirty_commits === false) {
156
+ result.permissions = {
157
+ customPolicies: {
158
+ autoCommits: config.auto_commits,
159
+ dirtyCommits: config.dirty_commits
160
+ }
161
+ };
162
+ }
163
+ return result;
164
+ } catch {
165
+ return {};
166
+ }
167
+ }
168
+ function parseGeminiCliConfig(content) {
169
+ try {
170
+ const config = JSON.parse(content);
171
+ const result = {
172
+ rawRedacted: redactSensitiveData(config)
173
+ };
174
+ if (config.safety || config.permissions) {
175
+ result.permissions = {
176
+ customPolicies: redactSensitiveData(config.safety || config.permissions)
177
+ };
178
+ }
179
+ return result;
180
+ } catch {
181
+ return {};
182
+ }
183
+ }
184
+ function parseOpencodeConfig(content) {
185
+ try {
186
+ const config = parseYaml(content, { strict: true });
187
+ const result = {
188
+ rawRedacted: redactSensitiveData(config)
189
+ };
190
+ if (config.security) {
191
+ result.permissions = {
192
+ allowedPaths: config.security.allowedPaths,
193
+ blockedCommands: config.security.blockedCommands,
194
+ customPolicies: redactSensitiveData(config.security)
195
+ };
196
+ }
197
+ return result;
198
+ } catch {
199
+ return {};
200
+ }
201
+ }
202
+ async function parseAgentConfig(agent, configPath) {
203
+ try {
204
+ const content = readFileSync(configPath, "utf-8");
205
+ switch (agent) {
206
+ case "claude-code":
207
+ return parseClaudeCodeConfig(content);
208
+ case "clawdbot":
209
+ if (content.trim().startsWith("{")) {
210
+ return parseMoltbotJsonConfig(content);
211
+ }
212
+ return parseClawdbotConfig(content);
213
+ case "moltbot":
214
+ return parseMoltbotJsonConfig(content);
215
+ case "aider":
216
+ return parseAiderConfig(content);
217
+ case "gemini-cli":
218
+ return parseGeminiCliConfig(content);
219
+ case "opencode":
220
+ return parseOpencodeConfig(content);
221
+ default:
222
+ try {
223
+ const config = content.trim().startsWith("{") ? JSON.parse(content) : parseYaml(content, { strict: true });
224
+ return {
225
+ rawRedacted: redactSensitiveData(config)
226
+ };
227
+ } catch {
228
+ return null;
229
+ }
230
+ }
231
+ } catch {
232
+ return null;
233
+ }
234
+ }
235
+ function formatRedactedConfig(config, indent = 0) {
236
+ const lines = [];
237
+ const prefix = " ".repeat(indent);
238
+ for (const [key, value] of Object.entries(config)) {
239
+ if (value === null || value === void 0) {
240
+ continue;
241
+ }
242
+ if (typeof value === "object" && !Array.isArray(value)) {
243
+ lines.push(`${prefix}${key}:`);
244
+ lines.push(formatRedactedConfig(value, indent + 1));
245
+ } else if (Array.isArray(value)) {
246
+ if (value.length === 0) {
247
+ lines.push(`${prefix}${key}: []`);
248
+ } else if (typeof value[0] === "object") {
249
+ lines.push(`${prefix}${key}:`);
250
+ for (const item of value) {
251
+ lines.push(`${prefix} -`);
252
+ lines.push(formatRedactedConfig(item, indent + 2));
253
+ }
254
+ } else {
255
+ lines.push(`${prefix}${key}: [${value.join(", ")}]`);
256
+ }
257
+ } else {
258
+ lines.push(`${prefix}${key}: ${value}`);
259
+ }
260
+ }
261
+ return lines.join("\n");
262
+ }
263
+
264
+ export {
265
+ redactSensitiveData,
266
+ parseAgentConfig,
267
+ formatRedactedConfig
268
+ };
269
+ //# sourceMappingURL=chunk-ID2O2QTI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/transparency/config-parser.ts"],"sourcesContent":["/**\r\n * Agent Config Parser with Sensitive Data Redaction\r\n * Parses YAML/JSON configs and redacts sensitive fields\r\n */\r\n\r\nimport { readFileSync } from 'fs'\r\nimport { parse as parseYaml } from 'yaml'\r\nimport type { AgentType, AgentPermissions } from '../types.js'\r\n\r\n// Patterns for sensitive field names that should be redacted\r\nconst SENSITIVE_FIELD_PATTERNS = [\r\n /^api[_-]?key$/i,\r\n /^apikey$/i,\r\n /^secret$/i,\r\n /^password$/i,\r\n /^credential[s]?$/i,\r\n /^token$/i,\r\n /^bearer$/i,\r\n /^auth$/i,\r\n /^private[_-]?key$/i,\r\n /_key$/i,\r\n /_token$/i,\r\n /_secret$/i,\r\n /_password$/i,\r\n /^access[_-]?token$/i,\r\n /^refresh[_-]?token$/i,\r\n /^client[_-]?secret$/i,\r\n /^encryption[_-]?key$/i\r\n]\r\n\r\n// Value patterns that look like secrets (even if field name doesn't match)\r\nconst SENSITIVE_VALUE_PATTERNS = [\r\n /^sk-[a-zA-Z0-9]{20,}$/, // OpenAI API key\r\n /^ghp_[a-zA-Z0-9]{36}$/, // GitHub personal access token\r\n /^gho_[a-zA-Z0-9]{36}$/, // GitHub OAuth token\r\n /^glpat-[a-zA-Z0-9\\-]{20,}$/, // GitLab personal access token\r\n /^xoxb-[a-zA-Z0-9\\-]+$/, // Slack bot token\r\n /^xoxp-[a-zA-Z0-9\\-]+$/, // Slack user token\r\n /^AKIA[A-Z0-9]{16}$/, // AWS access key\r\n /^-----BEGIN.*PRIVATE KEY-----/, // PEM private key\r\n /^Bearer\\s+[a-zA-Z0-9\\-._~+\\/]+=*$/i // Bearer token\r\n]\r\n\r\nconst REDACTED_MARKER = '[REDACTED]'\r\n\r\ninterface ParsedAgentConfig {\r\n permissions?: AgentPermissions\r\n hooks?: string[]\r\n rawRedacted?: Record<string, unknown>\r\n}\r\n\r\n/**\r\n * Check if a field name looks sensitive\r\n */\r\nfunction isSensitiveFieldName(name: string): boolean {\r\n return SENSITIVE_FIELD_PATTERNS.some(pattern => pattern.test(name))\r\n}\r\n\r\n/**\r\n * Check if a value looks like a secret\r\n */\r\nfunction isSensitiveValue(value: unknown): boolean {\r\n if (typeof value !== 'string') return false\r\n return SENSITIVE_VALUE_PATTERNS.some(pattern => pattern.test(value))\r\n}\r\n\r\n/**\r\n * Recursively redact sensitive fields from an object\r\n */\r\nexport function redactSensitiveData(obj: unknown, path = ''): unknown {\r\n if (obj === null || obj === undefined) {\r\n return obj\r\n }\r\n\r\n if (Array.isArray(obj)) {\r\n return obj.map((item, index) => redactSensitiveData(item, `${path}[${index}]`))\r\n }\r\n\r\n if (typeof obj === 'object') {\r\n const result: Record<string, unknown> = {}\r\n\r\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\r\n if (isSensitiveFieldName(key)) {\r\n result[key] = REDACTED_MARKER\r\n } else if (typeof value === 'string' && isSensitiveValue(value)) {\r\n result[key] = REDACTED_MARKER\r\n } else {\r\n result[key] = redactSensitiveData(value, `${path}.${key}`)\r\n }\r\n }\r\n\r\n return result\r\n }\r\n\r\n // For primitive values, check if they look like secrets\r\n if (typeof obj === 'string' && isSensitiveValue(obj)) {\r\n return REDACTED_MARKER\r\n }\r\n\r\n return obj\r\n}\r\n\r\n/**\r\n * Parse Claude Code settings.json\r\n */\r\nfunction parseClaudeCodeConfig(content: string): ParsedAgentConfig {\r\n try {\r\n const config = JSON.parse(content)\r\n const result: ParsedAgentConfig = {\r\n rawRedacted: redactSensitiveData(config) as Record<string, unknown>\r\n }\r\n\r\n // Extract hooks\r\n if (config.hooks) {\r\n result.hooks = Object.keys(config.hooks)\r\n }\r\n\r\n // Claude Code doesn't have traditional permissions in config\r\n // but we can extract any security-related settings\r\n if (config.security || config.permissions) {\r\n result.permissions = {\r\n customPolicies: redactSensitiveData(config.security || config.permissions) as Record<string, unknown>\r\n }\r\n }\r\n\r\n return result\r\n } catch {\r\n return {}\r\n }\r\n}\r\n\r\n/**\r\n * Parse Clawdbot config.yml (legacy YAML format)\r\n */\r\nfunction parseClawdbotConfig(content: string): ParsedAgentConfig {\r\n try {\r\n const config = parseYaml(content, { strict: true })\r\n const result: ParsedAgentConfig = {\r\n rawRedacted: redactSensitiveData(config) as Record<string, unknown>\r\n }\r\n\r\n // Extract permissions from security section\r\n if (config.security) {\r\n result.permissions = {\r\n allowedPaths: config.security.allowedPaths,\r\n blockedCommands: config.security.blockedCommands,\r\n securityProfile: config.security.profile,\r\n customPolicies: redactSensitiveData(config.security) as Record<string, unknown>\r\n }\r\n\r\n if (config.security.riskThreshold) {\r\n result.permissions.rateLimit = config.security.riskThreshold\r\n }\r\n }\r\n\r\n // Extract hooks\r\n if (config.hooks) {\r\n result.hooks = Object.keys(config.hooks)\r\n }\r\n\r\n return result\r\n } catch {\r\n return {}\r\n }\r\n}\r\n\r\n/**\r\n * Parse Moltbot moltbot.json (new JSON format)\r\n */\r\nfunction parseMoltbotJsonConfig(content: string): ParsedAgentConfig {\r\n try {\r\n const config = JSON.parse(content)\r\n const result: ParsedAgentConfig = {\r\n rawRedacted: redactSensitiveData(config) as Record<string, unknown>\r\n }\r\n\r\n // Extract gateway settings\r\n const gateway = config.gateway || {}\r\n\r\n // Extract security settings from agents.defaults.sandbox\r\n const sandbox = config.agents?.defaults?.sandbox || {}\r\n\r\n // Build permissions object\r\n result.permissions = {\r\n securityProfile: sandbox.mode,\r\n customPolicies: {\r\n gateway: redactSensitiveData(gateway) as Record<string, unknown>,\r\n sandbox: redactSensitiveData(sandbox) as Record<string, unknown>\r\n }\r\n }\r\n\r\n // Extract allowed/blocked from security config if present\r\n if (config.security) {\r\n result.permissions.allowedPaths = config.security.allowedPaths\r\n result.permissions.blockedCommands = config.security.blockedCommands\r\n }\r\n\r\n // Extract hooks\r\n if (config.hooks) {\r\n result.hooks = Object.keys(config.hooks)\r\n }\r\n\r\n return result\r\n } catch {\r\n return {}\r\n }\r\n}\r\n\r\n/**\r\n * Parse Aider config\r\n */\r\nfunction parseAiderConfig(content: string): ParsedAgentConfig {\r\n try {\r\n const config = parseYaml(content, { strict: true })\r\n const result: ParsedAgentConfig = {\r\n rawRedacted: redactSensitiveData(config) as Record<string, unknown>\r\n }\r\n\r\n // Aider has different config structure\r\n // Extract relevant security settings if present\r\n if (config.auto_commits === false || config.dirty_commits === false) {\r\n result.permissions = {\r\n customPolicies: {\r\n autoCommits: config.auto_commits,\r\n dirtyCommits: config.dirty_commits\r\n }\r\n }\r\n }\r\n\r\n return result\r\n } catch {\r\n return {}\r\n }\r\n}\r\n\r\n/**\r\n * Parse Gemini CLI config\r\n */\r\nfunction parseGeminiCliConfig(content: string): ParsedAgentConfig {\r\n try {\r\n const config = JSON.parse(content)\r\n const result: ParsedAgentConfig = {\r\n rawRedacted: redactSensitiveData(config) as Record<string, unknown>\r\n }\r\n\r\n // Extract any security/permission settings\r\n if (config.safety || config.permissions) {\r\n result.permissions = {\r\n customPolicies: redactSensitiveData(config.safety || config.permissions) as Record<string, unknown>\r\n }\r\n }\r\n\r\n return result\r\n } catch {\r\n return {}\r\n }\r\n}\r\n\r\n/**\r\n * Parse OpenCode config\r\n */\r\nfunction parseOpencodeConfig(content: string): ParsedAgentConfig {\r\n try {\r\n const config = parseYaml(content, { strict: true })\r\n const result: ParsedAgentConfig = {\r\n rawRedacted: redactSensitiveData(config) as Record<string, unknown>\r\n }\r\n\r\n // Extract security settings\r\n if (config.security) {\r\n result.permissions = {\r\n allowedPaths: config.security.allowedPaths,\r\n blockedCommands: config.security.blockedCommands,\r\n customPolicies: redactSensitiveData(config.security) as Record<string, unknown>\r\n }\r\n }\r\n\r\n return result\r\n } catch {\r\n return {}\r\n }\r\n}\r\n\r\n/**\r\n * Parse agent configuration file\r\n */\r\nexport async function parseAgentConfig(\r\n agent: AgentType,\r\n configPath: string\r\n): Promise<ParsedAgentConfig | null> {\r\n try {\r\n const content = readFileSync(configPath, 'utf-8')\r\n\r\n switch (agent) {\r\n case 'claude-code':\r\n return parseClaudeCodeConfig(content)\r\n case 'clawdbot':\r\n // Check if it's JSON (moltbot.json) or YAML (config.yml)\r\n if (content.trim().startsWith('{')) {\r\n return parseMoltbotJsonConfig(content)\r\n }\r\n return parseClawdbotConfig(content)\r\n case 'moltbot':\r\n return parseMoltbotJsonConfig(content)\r\n case 'aider':\r\n return parseAiderConfig(content)\r\n case 'gemini-cli':\r\n return parseGeminiCliConfig(content)\r\n case 'opencode':\r\n return parseOpencodeConfig(content)\r\n default:\r\n // Generic YAML/JSON parser\r\n try {\r\n const config = content.trim().startsWith('{')\r\n ? JSON.parse(content)\r\n : parseYaml(content, { strict: true })\r\n\r\n return {\r\n rawRedacted: redactSensitiveData(config) as Record<string, unknown>\r\n }\r\n } catch {\r\n return null\r\n }\r\n }\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * Get a safe string representation of config for display\r\n */\r\nexport function formatRedactedConfig(\r\n config: Record<string, unknown>,\r\n indent = 0\r\n): string {\r\n const lines: string[] = []\r\n const prefix = ' '.repeat(indent)\r\n\r\n for (const [key, value] of Object.entries(config)) {\r\n if (value === null || value === undefined) {\r\n continue\r\n }\r\n\r\n if (typeof value === 'object' && !Array.isArray(value)) {\r\n lines.push(`${prefix}${key}:`)\r\n lines.push(formatRedactedConfig(value as Record<string, unknown>, indent + 1))\r\n } else if (Array.isArray(value)) {\r\n if (value.length === 0) {\r\n lines.push(`${prefix}${key}: []`)\r\n } else if (typeof value[0] === 'object') {\r\n lines.push(`${prefix}${key}:`)\r\n for (const item of value) {\r\n lines.push(`${prefix} -`)\r\n lines.push(formatRedactedConfig(item as Record<string, unknown>, indent + 2))\r\n }\r\n } else {\r\n lines.push(`${prefix}${key}: [${value.join(', ')}]`)\r\n }\r\n } else {\r\n lines.push(`${prefix}${key}: ${value}`)\r\n }\r\n }\r\n\r\n return lines.join('\\n')\r\n}\r\n"],"mappings":"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","names":[]}
@@ -0,0 +1,357 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/hooks/moltbot.ts
4
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
5
+ import { join } from "path";
6
+ import { homedir } from "os";
7
+ import { execFileSync } from "child_process";
8
+ var CONFIG_PATHS = [
9
+ join(homedir(), ".moltbot", "config.json"),
10
+ join(homedir(), ".clawdbot", "moltbot.json"),
11
+ join(homedir(), ".config", "moltbot", "config.json")
12
+ ];
13
+ var MOLTBOT_DIR = join(homedir(), ".moltbot");
14
+ var CLAWDBOT_DIR = join(homedir(), ".clawdbot");
15
+ var BASHBROS_HOOK_MARKER = "# bashbros-managed";
16
+ var DEFAULT_GATEWAY_PORT = 18789;
17
+ var MoltbotHooks = class {
18
+ /**
19
+ * Check if moltbot command is installed
20
+ */
21
+ static isMoltbotInstalled() {
22
+ try {
23
+ const cmd = process.platform === "win32" ? "where" : "which";
24
+ execFileSync(cmd, ["moltbot"], { stdio: "pipe", timeout: 3e3 });
25
+ return true;
26
+ } catch {
27
+ return false;
28
+ }
29
+ }
30
+ /**
31
+ * Check if clawdbot command is installed (legacy)
32
+ */
33
+ static isClawdbotInstalled() {
34
+ try {
35
+ const cmd = process.platform === "win32" ? "where" : "which";
36
+ execFileSync(cmd, ["clawdbot"], { stdio: "pipe", timeout: 3e3 });
37
+ return true;
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
42
+ /**
43
+ * Find the config file path
44
+ */
45
+ static findConfigPath() {
46
+ const envPath = process.env.CLAWDBOT_CONFIG_PATH;
47
+ if (envPath && existsSync(envPath)) {
48
+ return envPath;
49
+ }
50
+ for (const configPath of CONFIG_PATHS) {
51
+ if (existsSync(configPath)) {
52
+ return configPath;
53
+ }
54
+ }
55
+ return null;
56
+ }
57
+ /**
58
+ * Get the config directory (creating if needed)
59
+ */
60
+ static getConfigDir() {
61
+ if (this.isMoltbotInstalled() || existsSync(MOLTBOT_DIR)) {
62
+ return MOLTBOT_DIR;
63
+ }
64
+ return CLAWDBOT_DIR;
65
+ }
66
+ /**
67
+ * Load current moltbot settings
68
+ */
69
+ static loadSettings() {
70
+ const configPath = this.findConfigPath();
71
+ if (!configPath) {
72
+ return {};
73
+ }
74
+ try {
75
+ const content = readFileSync(configPath, "utf-8");
76
+ return JSON.parse(content);
77
+ } catch {
78
+ return {};
79
+ }
80
+ }
81
+ /**
82
+ * Save moltbot settings
83
+ */
84
+ static saveSettings(settings) {
85
+ const configDir = this.getConfigDir();
86
+ if (!existsSync(configDir)) {
87
+ mkdirSync(configDir, { recursive: true });
88
+ }
89
+ const configPath = join(configDir, this.isMoltbotInstalled() ? "config.json" : "moltbot.json");
90
+ writeFileSync(configPath, JSON.stringify(settings, null, 2), "utf-8");
91
+ }
92
+ /**
93
+ * Install BashBros hooks into moltbot
94
+ */
95
+ static install() {
96
+ if (!this.isMoltbotInstalled() && !this.isClawdbotInstalled()) {
97
+ return {
98
+ success: false,
99
+ message: "Neither moltbot nor clawdbot found. Install moltbot first."
100
+ };
101
+ }
102
+ const settings = this.loadSettings();
103
+ if (this.isInstalled(settings)) {
104
+ return {
105
+ success: true,
106
+ message: "BashBros hooks already installed."
107
+ };
108
+ }
109
+ if (!settings.hooks) {
110
+ settings.hooks = {};
111
+ }
112
+ const preBashHook = {
113
+ command: `bashbros gate "$COMMAND" ${BASHBROS_HOOK_MARKER}`
114
+ };
115
+ const postBashHook = {
116
+ command: `bashbros record "$COMMAND" ${BASHBROS_HOOK_MARKER}`
117
+ };
118
+ const sessionEndHook = {
119
+ command: `bashbros session-end ${BASHBROS_HOOK_MARKER}`
120
+ };
121
+ settings.hooks.preBash = [
122
+ ...settings.hooks.preBash || [],
123
+ preBashHook
124
+ ];
125
+ settings.hooks.postBash = [
126
+ ...settings.hooks.postBash || [],
127
+ postBashHook
128
+ ];
129
+ settings.hooks.sessionEnd = [
130
+ ...settings.hooks.sessionEnd || [],
131
+ sessionEndHook
132
+ ];
133
+ this.saveSettings(settings);
134
+ return {
135
+ success: true,
136
+ message: "BashBros hooks installed successfully."
137
+ };
138
+ }
139
+ /**
140
+ * Uninstall BashBros hooks from moltbot
141
+ */
142
+ static uninstall() {
143
+ const configPath = this.findConfigPath();
144
+ if (!configPath) {
145
+ return {
146
+ success: true,
147
+ message: "No moltbot config found. Nothing to uninstall."
148
+ };
149
+ }
150
+ const settings = this.loadSettings();
151
+ if (!settings.hooks) {
152
+ return {
153
+ success: true,
154
+ message: "No hooks to uninstall."
155
+ };
156
+ }
157
+ const filterHooks = (hooks) => {
158
+ if (!hooks) return [];
159
+ return hooks.filter((h) => !h.command.includes(BASHBROS_HOOK_MARKER));
160
+ };
161
+ settings.hooks.preBash = filterHooks(settings.hooks.preBash);
162
+ settings.hooks.postBash = filterHooks(settings.hooks.postBash);
163
+ settings.hooks.sessionEnd = filterHooks(settings.hooks.sessionEnd);
164
+ if (settings.hooks.preBash?.length === 0) delete settings.hooks.preBash;
165
+ if (settings.hooks.postBash?.length === 0) delete settings.hooks.postBash;
166
+ if (settings.hooks.sessionEnd?.length === 0) delete settings.hooks.sessionEnd;
167
+ if (Object.keys(settings.hooks).length === 0) delete settings.hooks;
168
+ this.saveSettings(settings);
169
+ return {
170
+ success: true,
171
+ message: "BashBros hooks uninstalled successfully."
172
+ };
173
+ }
174
+ /**
175
+ * Check if BashBros hooks are installed
176
+ */
177
+ static isInstalled(settings) {
178
+ const s = settings || this.loadSettings();
179
+ if (!s.hooks) return false;
180
+ const hasMarker = (hooks) => {
181
+ if (!hooks) return false;
182
+ return hooks.some((h) => h.command.includes(BASHBROS_HOOK_MARKER));
183
+ };
184
+ return hasMarker(s.hooks.preBash) || hasMarker(s.hooks.postBash) || hasMarker(s.hooks.sessionEnd);
185
+ }
186
+ /**
187
+ * Check if moltbot gateway is running
188
+ */
189
+ static async isGatewayRunning() {
190
+ const settings = this.loadSettings();
191
+ const port = settings.gateway?.port || DEFAULT_GATEWAY_PORT;
192
+ const host = settings.gateway?.host || "localhost";
193
+ try {
194
+ const controller = new AbortController();
195
+ const timeout = setTimeout(() => controller.abort(), 2e3);
196
+ const response = await fetch(`http://${host}:${port}/health`, {
197
+ method: "GET",
198
+ signal: controller.signal
199
+ });
200
+ clearTimeout(timeout);
201
+ return response.ok;
202
+ } catch {
203
+ return false;
204
+ }
205
+ }
206
+ /**
207
+ * Get gateway status
208
+ */
209
+ static async getGatewayStatus() {
210
+ const settings = this.loadSettings();
211
+ const port = settings.gateway?.port || DEFAULT_GATEWAY_PORT;
212
+ const host = settings.gateway?.host || "localhost";
213
+ const sandboxMode = settings.agents?.defaults?.sandbox?.mode === "strict";
214
+ const running = await this.isGatewayRunning();
215
+ return {
216
+ running,
217
+ port,
218
+ host,
219
+ sandboxMode,
220
+ error: running ? void 0 : "Gateway not responding"
221
+ };
222
+ }
223
+ /**
224
+ * Get gateway info (for type system)
225
+ */
226
+ static async getGatewayInfo() {
227
+ const status = await this.getGatewayStatus();
228
+ if (!status.running) return null;
229
+ return {
230
+ port: status.port,
231
+ host: status.host,
232
+ sandboxMode: status.sandboxMode,
233
+ authToken: !!this.loadSettings().gateway?.auth
234
+ };
235
+ }
236
+ /**
237
+ * Run security audit using moltbot CLI
238
+ */
239
+ static async runSecurityAudit() {
240
+ const findings = [];
241
+ const timestamp = /* @__PURE__ */ new Date();
242
+ if (!this.isMoltbotInstalled() && !this.isClawdbotInstalled()) {
243
+ return {
244
+ passed: false,
245
+ findings: [{
246
+ severity: "critical",
247
+ category: "installation",
248
+ message: "Moltbot/clawdbot not installed",
249
+ recommendation: "Install moltbot to enable security auditing"
250
+ }],
251
+ timestamp
252
+ };
253
+ }
254
+ try {
255
+ const cmd = this.isMoltbotInstalled() ? "moltbot" : "clawdbot";
256
+ const output = execFileSync(cmd, ["security", "audit", "--deep", "--json"], {
257
+ encoding: "utf-8",
258
+ timeout: 3e4,
259
+ stdio: ["pipe", "pipe", "pipe"]
260
+ });
261
+ const auditResult = JSON.parse(output);
262
+ if (auditResult.findings && Array.isArray(auditResult.findings)) {
263
+ for (const finding of auditResult.findings) {
264
+ findings.push({
265
+ severity: finding.severity || "info",
266
+ category: finding.category || "general",
267
+ message: finding.message || "Unknown finding",
268
+ recommendation: finding.recommendation
269
+ });
270
+ }
271
+ }
272
+ return {
273
+ passed: findings.filter((f) => f.severity === "critical").length === 0,
274
+ findings,
275
+ timestamp
276
+ };
277
+ } catch (error) {
278
+ return this.runBasicSecurityChecks();
279
+ }
280
+ }
281
+ /**
282
+ * Run basic security checks when moltbot audit is not available
283
+ */
284
+ static runBasicSecurityChecks() {
285
+ const findings = [];
286
+ const settings = this.loadSettings();
287
+ if (!this.isInstalled(settings)) {
288
+ findings.push({
289
+ severity: "warning",
290
+ category: "hooks",
291
+ message: "BashBros hooks not installed",
292
+ recommendation: 'Run "bashbros moltbot install" to enable command gating'
293
+ });
294
+ }
295
+ const sandboxMode = settings.agents?.defaults?.sandbox?.mode;
296
+ if (!sandboxMode || sandboxMode === "off") {
297
+ findings.push({
298
+ severity: "warning",
299
+ category: "sandbox",
300
+ message: "Sandbox mode is disabled",
301
+ recommendation: "Enable sandbox mode in moltbot config for additional protection"
302
+ });
303
+ }
304
+ if (settings.gateway) {
305
+ if (!settings.gateway.auth) {
306
+ findings.push({
307
+ severity: "info",
308
+ category: "gateway",
309
+ message: "Gateway authentication not configured",
310
+ recommendation: "Consider enabling gateway authentication for multi-user environments"
311
+ });
312
+ }
313
+ }
314
+ return {
315
+ passed: findings.filter((f) => f.severity === "critical").length === 0,
316
+ findings,
317
+ timestamp: /* @__PURE__ */ new Date()
318
+ };
319
+ }
320
+ /**
321
+ * Get comprehensive hook status
322
+ */
323
+ static getStatus() {
324
+ const moltbotInstalled = this.isMoltbotInstalled();
325
+ const clawdbotInstalled = this.isClawdbotInstalled();
326
+ const configPath = this.findConfigPath();
327
+ const settings = this.loadSettings();
328
+ const hooksInstalled = this.isInstalled(settings);
329
+ const hooks = [];
330
+ if (settings.hooks?.preBash) hooks.push("preBash (gate)");
331
+ if (settings.hooks?.postBash) hooks.push("postBash (record)");
332
+ if (settings.hooks?.sessionEnd) hooks.push("sessionEnd (report)");
333
+ const sandboxMode = settings.agents?.defaults?.sandbox?.mode || null;
334
+ let gatewayRunning = false;
335
+ if (settings.gateway?.port) {
336
+ gatewayRunning = true;
337
+ }
338
+ return {
339
+ moltbotInstalled,
340
+ clawdbotInstalled,
341
+ hooksInstalled,
342
+ hooks,
343
+ configPath,
344
+ gatewayRunning,
345
+ sandboxMode
346
+ };
347
+ }
348
+ };
349
+ function getMoltbotHooks() {
350
+ return MoltbotHooks;
351
+ }
352
+
353
+ export {
354
+ MoltbotHooks,
355
+ getMoltbotHooks
356
+ };
357
+ //# sourceMappingURL=chunk-J37RHCFJ.js.map