mstro-app 0.1.47
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.
- package/LICENSE +21 -0
- package/README.md +177 -0
- package/bin/commands/config.js +145 -0
- package/bin/commands/login.js +313 -0
- package/bin/commands/logout.js +75 -0
- package/bin/commands/status.js +197 -0
- package/bin/commands/whoami.js +161 -0
- package/bin/configure-claude.js +298 -0
- package/bin/mstro.js +581 -0
- package/bin/postinstall.js +45 -0
- package/bin/release.sh +110 -0
- package/dist/server/cli/headless/claude-invoker.d.ts +17 -0
- package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -0
- package/dist/server/cli/headless/claude-invoker.js +311 -0
- package/dist/server/cli/headless/claude-invoker.js.map +1 -0
- package/dist/server/cli/headless/index.d.ts +13 -0
- package/dist/server/cli/headless/index.d.ts.map +1 -0
- package/dist/server/cli/headless/index.js +10 -0
- package/dist/server/cli/headless/index.js.map +1 -0
- package/dist/server/cli/headless/mcp-config.d.ts +11 -0
- package/dist/server/cli/headless/mcp-config.d.ts.map +1 -0
- package/dist/server/cli/headless/mcp-config.js +76 -0
- package/dist/server/cli/headless/mcp-config.js.map +1 -0
- package/dist/server/cli/headless/output-utils.d.ts +33 -0
- package/dist/server/cli/headless/output-utils.d.ts.map +1 -0
- package/dist/server/cli/headless/output-utils.js +101 -0
- package/dist/server/cli/headless/output-utils.js.map +1 -0
- package/dist/server/cli/headless/prompt-utils.d.ts +21 -0
- package/dist/server/cli/headless/prompt-utils.d.ts.map +1 -0
- package/dist/server/cli/headless/prompt-utils.js +84 -0
- package/dist/server/cli/headless/prompt-utils.js.map +1 -0
- package/dist/server/cli/headless/runner.d.ts +24 -0
- package/dist/server/cli/headless/runner.d.ts.map +1 -0
- package/dist/server/cli/headless/runner.js +99 -0
- package/dist/server/cli/headless/runner.js.map +1 -0
- package/dist/server/cli/headless/types.d.ts +106 -0
- package/dist/server/cli/headless/types.d.ts.map +1 -0
- package/dist/server/cli/headless/types.js +4 -0
- package/dist/server/cli/headless/types.js.map +1 -0
- package/dist/server/cli/improvisation-session-manager.d.ts +155 -0
- package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -0
- package/dist/server/cli/improvisation-session-manager.js +415 -0
- package/dist/server/cli/improvisation-session-manager.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +386 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/mcp/bouncer-cli.d.ts +3 -0
- package/dist/server/mcp/bouncer-cli.d.ts.map +1 -0
- package/dist/server/mcp/bouncer-cli.js +99 -0
- package/dist/server/mcp/bouncer-cli.js.map +1 -0
- package/dist/server/mcp/bouncer-integration.d.ts +36 -0
- package/dist/server/mcp/bouncer-integration.d.ts.map +1 -0
- package/dist/server/mcp/bouncer-integration.js +301 -0
- package/dist/server/mcp/bouncer-integration.js.map +1 -0
- package/dist/server/mcp/security-audit.d.ts +52 -0
- package/dist/server/mcp/security-audit.d.ts.map +1 -0
- package/dist/server/mcp/security-audit.js +118 -0
- package/dist/server/mcp/security-audit.js.map +1 -0
- package/dist/server/mcp/security-patterns.d.ts +73 -0
- package/dist/server/mcp/security-patterns.d.ts.map +1 -0
- package/dist/server/mcp/security-patterns.js +247 -0
- package/dist/server/mcp/security-patterns.js.map +1 -0
- package/dist/server/mcp/server.d.ts +3 -0
- package/dist/server/mcp/server.d.ts.map +1 -0
- package/dist/server/mcp/server.js +146 -0
- package/dist/server/mcp/server.js.map +1 -0
- package/dist/server/routes/files.d.ts +9 -0
- package/dist/server/routes/files.d.ts.map +1 -0
- package/dist/server/routes/files.js +24 -0
- package/dist/server/routes/files.js.map +1 -0
- package/dist/server/routes/improvise.d.ts +3 -0
- package/dist/server/routes/improvise.d.ts.map +1 -0
- package/dist/server/routes/improvise.js +72 -0
- package/dist/server/routes/improvise.js.map +1 -0
- package/dist/server/routes/index.d.ts +10 -0
- package/dist/server/routes/index.d.ts.map +1 -0
- package/dist/server/routes/index.js +12 -0
- package/dist/server/routes/index.js.map +1 -0
- package/dist/server/routes/instances.d.ts +10 -0
- package/dist/server/routes/instances.d.ts.map +1 -0
- package/dist/server/routes/instances.js +47 -0
- package/dist/server/routes/instances.js.map +1 -0
- package/dist/server/routes/notifications.d.ts +3 -0
- package/dist/server/routes/notifications.d.ts.map +1 -0
- package/dist/server/routes/notifications.js +136 -0
- package/dist/server/routes/notifications.js.map +1 -0
- package/dist/server/services/analytics.d.ts +56 -0
- package/dist/server/services/analytics.d.ts.map +1 -0
- package/dist/server/services/analytics.js +240 -0
- package/dist/server/services/analytics.js.map +1 -0
- package/dist/server/services/auth.d.ts +26 -0
- package/dist/server/services/auth.d.ts.map +1 -0
- package/dist/server/services/auth.js +71 -0
- package/dist/server/services/auth.js.map +1 -0
- package/dist/server/services/client-id.d.ts +10 -0
- package/dist/server/services/client-id.d.ts.map +1 -0
- package/dist/server/services/client-id.js +61 -0
- package/dist/server/services/client-id.js.map +1 -0
- package/dist/server/services/credentials.d.ts +39 -0
- package/dist/server/services/credentials.d.ts.map +1 -0
- package/dist/server/services/credentials.js +110 -0
- package/dist/server/services/credentials.js.map +1 -0
- package/dist/server/services/files.d.ts +119 -0
- package/dist/server/services/files.d.ts.map +1 -0
- package/dist/server/services/files.js +560 -0
- package/dist/server/services/files.js.map +1 -0
- package/dist/server/services/instances.d.ts +52 -0
- package/dist/server/services/instances.d.ts.map +1 -0
- package/dist/server/services/instances.js +241 -0
- package/dist/server/services/instances.js.map +1 -0
- package/dist/server/services/pathUtils.d.ts +47 -0
- package/dist/server/services/pathUtils.d.ts.map +1 -0
- package/dist/server/services/pathUtils.js +124 -0
- package/dist/server/services/pathUtils.js.map +1 -0
- package/dist/server/services/platform.d.ts +72 -0
- package/dist/server/services/platform.d.ts.map +1 -0
- package/dist/server/services/platform.js +368 -0
- package/dist/server/services/platform.js.map +1 -0
- package/dist/server/services/sentry.d.ts +5 -0
- package/dist/server/services/sentry.d.ts.map +1 -0
- package/dist/server/services/sentry.js +71 -0
- package/dist/server/services/sentry.js.map +1 -0
- package/dist/server/services/terminal/pty-manager.d.ts +149 -0
- package/dist/server/services/terminal/pty-manager.d.ts.map +1 -0
- package/dist/server/services/terminal/pty-manager.js +377 -0
- package/dist/server/services/terminal/pty-manager.js.map +1 -0
- package/dist/server/services/terminal/tmux-manager.d.ts +82 -0
- package/dist/server/services/terminal/tmux-manager.d.ts.map +1 -0
- package/dist/server/services/terminal/tmux-manager.js +352 -0
- package/dist/server/services/terminal/tmux-manager.js.map +1 -0
- package/dist/server/services/websocket/autocomplete.d.ts +50 -0
- package/dist/server/services/websocket/autocomplete.d.ts.map +1 -0
- package/dist/server/services/websocket/autocomplete.js +361 -0
- package/dist/server/services/websocket/autocomplete.js.map +1 -0
- package/dist/server/services/websocket/file-utils.d.ts +44 -0
- package/dist/server/services/websocket/file-utils.d.ts.map +1 -0
- package/dist/server/services/websocket/file-utils.js +272 -0
- package/dist/server/services/websocket/file-utils.js.map +1 -0
- package/dist/server/services/websocket/handler.d.ts +246 -0
- package/dist/server/services/websocket/handler.d.ts.map +1 -0
- package/dist/server/services/websocket/handler.js +1771 -0
- package/dist/server/services/websocket/handler.js.map +1 -0
- package/dist/server/services/websocket/index.d.ts +11 -0
- package/dist/server/services/websocket/index.d.ts.map +1 -0
- package/dist/server/services/websocket/index.js +14 -0
- package/dist/server/services/websocket/index.js.map +1 -0
- package/dist/server/services/websocket/types.d.ts +214 -0
- package/dist/server/services/websocket/types.d.ts.map +1 -0
- package/dist/server/services/websocket/types.js +4 -0
- package/dist/server/services/websocket/types.js.map +1 -0
- package/dist/server/utils/agent-manager.d.ts +69 -0
- package/dist/server/utils/agent-manager.d.ts.map +1 -0
- package/dist/server/utils/agent-manager.js +269 -0
- package/dist/server/utils/agent-manager.js.map +1 -0
- package/dist/server/utils/paths.d.ts +25 -0
- package/dist/server/utils/paths.d.ts.map +1 -0
- package/dist/server/utils/paths.js +38 -0
- package/dist/server/utils/paths.js.map +1 -0
- package/dist/server/utils/port-manager.d.ts +10 -0
- package/dist/server/utils/port-manager.d.ts.map +1 -0
- package/dist/server/utils/port-manager.js +60 -0
- package/dist/server/utils/port-manager.js.map +1 -0
- package/dist/server/utils/port.d.ts +26 -0
- package/dist/server/utils/port.d.ts.map +1 -0
- package/dist/server/utils/port.js +83 -0
- package/dist/server/utils/port.js.map +1 -0
- package/hooks/bouncer.sh +138 -0
- package/package.json +74 -0
- package/server/README.md +191 -0
- package/server/cli/headless/claude-invoker.ts +415 -0
- package/server/cli/headless/index.ts +39 -0
- package/server/cli/headless/mcp-config.ts +87 -0
- package/server/cli/headless/output-utils.ts +109 -0
- package/server/cli/headless/prompt-utils.ts +108 -0
- package/server/cli/headless/runner.ts +133 -0
- package/server/cli/headless/types.ts +118 -0
- package/server/cli/improvisation-session-manager.ts +531 -0
- package/server/index.ts +456 -0
- package/server/mcp/README.md +122 -0
- package/server/mcp/bouncer-cli.ts +127 -0
- package/server/mcp/bouncer-integration.ts +430 -0
- package/server/mcp/security-audit.ts +180 -0
- package/server/mcp/security-patterns.ts +290 -0
- package/server/mcp/server.ts +174 -0
- package/server/routes/files.ts +29 -0
- package/server/routes/improvise.ts +82 -0
- package/server/routes/index.ts +13 -0
- package/server/routes/instances.ts +54 -0
- package/server/routes/notifications.ts +158 -0
- package/server/services/analytics.ts +277 -0
- package/server/services/auth.ts +80 -0
- package/server/services/client-id.ts +68 -0
- package/server/services/credentials.ts +134 -0
- package/server/services/files.ts +710 -0
- package/server/services/instances.ts +275 -0
- package/server/services/pathUtils.ts +158 -0
- package/server/services/platform.test.ts +1314 -0
- package/server/services/platform.ts +435 -0
- package/server/services/sentry.ts +81 -0
- package/server/services/terminal/pty-manager.ts +464 -0
- package/server/services/terminal/tmux-manager.ts +426 -0
- package/server/services/websocket/autocomplete.ts +438 -0
- package/server/services/websocket/file-utils.ts +305 -0
- package/server/services/websocket/handler.test.ts +20 -0
- package/server/services/websocket/handler.ts +2047 -0
- package/server/services/websocket/index.ts +40 -0
- package/server/services/websocket/types.ts +339 -0
- package/server/tsconfig.json +19 -0
- package/server/utils/agent-manager.ts +323 -0
- package/server/utils/paths.ts +45 -0
- package/server/utils/port-manager.ts +70 -0
- package/server/utils/port.ts +102 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
3
|
+
// Licensed under the MIT License. See LICENSE file for details.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Mstro Claude Configuration Tool
|
|
7
|
+
*
|
|
8
|
+
* Automatically configures ~/.claude/settings.json and installs
|
|
9
|
+
* the bouncer hook for Claude Code integration.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* npx mstro configure-hooks
|
|
13
|
+
* node bin/configure-claude.js
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
17
|
+
import { homedir } from 'node:os';
|
|
18
|
+
import { dirname, join, resolve } from 'node:path';
|
|
19
|
+
import { createInterface } from 'node:readline';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
|
|
22
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
23
|
+
const __dirname = dirname(__filename);
|
|
24
|
+
const MSTRO_ROOT = resolve(__dirname, '..');
|
|
25
|
+
|
|
26
|
+
const CLAUDE_DIR = join(homedir(), '.claude');
|
|
27
|
+
const CLAUDE_HOOKS_DIR = join(CLAUDE_DIR, 'hooks');
|
|
28
|
+
const CLAUDE_SETTINGS_FILE = join(CLAUDE_DIR, 'settings.json');
|
|
29
|
+
const _BOUNCER_HOOK_SOURCE = join(MSTRO_ROOT, 'hooks', 'bouncer.sh');
|
|
30
|
+
const BOUNCER_CLI_PATH = join(MSTRO_ROOT, 'server', 'mcp', 'bouncer-cli.ts');
|
|
31
|
+
const TSX_PATH = join(MSTRO_ROOT, 'node_modules', '.bin', 'tsx');
|
|
32
|
+
|
|
33
|
+
// ANSI colors
|
|
34
|
+
const colors = {
|
|
35
|
+
reset: '\x1b[0m',
|
|
36
|
+
bold: '\x1b[1m',
|
|
37
|
+
green: '\x1b[32m',
|
|
38
|
+
yellow: '\x1b[33m',
|
|
39
|
+
blue: '\x1b[34m',
|
|
40
|
+
red: '\x1b[31m',
|
|
41
|
+
dim: '\x1b[2m',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function log(msg, color = '') {
|
|
45
|
+
console.log(`${color}${msg}${colors.reset}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function prompt(question) {
|
|
49
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
50
|
+
return new Promise((resolve) => {
|
|
51
|
+
rl.question(question, (answer) => {
|
|
52
|
+
rl.close();
|
|
53
|
+
resolve(answer.trim().toLowerCase());
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Generate the bouncer hook script with embedded paths
|
|
60
|
+
* This ensures the hook knows where to find the bouncer CLI regardless of
|
|
61
|
+
* how/where mstro was installed (npm global, npx, etc.)
|
|
62
|
+
*/
|
|
63
|
+
function generateBouncerHook(tsxPath, bouncerCliPath) {
|
|
64
|
+
return `#!/usr/bin/env bash
|
|
65
|
+
#
|
|
66
|
+
# Mstro Bouncer Gate - Claude Code Hook
|
|
67
|
+
#
|
|
68
|
+
# This hook intercepts Claude Code tool calls and routes them through
|
|
69
|
+
# the Mstro bouncer for security analysis before execution.
|
|
70
|
+
#
|
|
71
|
+
# Generated by: npx mstro configure-hooks
|
|
72
|
+
# Dependencies: Node.js (no jq or bun required)
|
|
73
|
+
#
|
|
74
|
+
|
|
75
|
+
set -euo pipefail
|
|
76
|
+
|
|
77
|
+
# Paths configured at install time
|
|
78
|
+
TSX_PATH="${tsxPath}"
|
|
79
|
+
BOUNCER_CLI="${bouncerCliPath}"
|
|
80
|
+
|
|
81
|
+
# User-configurable settings
|
|
82
|
+
BOUNCER_TIMEOUT="\${BOUNCER_TIMEOUT:-10}"
|
|
83
|
+
BOUNCER_LOG="\${BOUNCER_LOG:-$HOME/.claude/logs/bouncer.log}"
|
|
84
|
+
|
|
85
|
+
# Ensure log directory exists
|
|
86
|
+
mkdir -p "$(dirname "$BOUNCER_LOG")"
|
|
87
|
+
|
|
88
|
+
# Read hook input from stdin
|
|
89
|
+
INPUT=$(cat)
|
|
90
|
+
|
|
91
|
+
# Run the bouncer via tsx (handles TypeScript execution)
|
|
92
|
+
if [ -x "$TSX_PATH" ] && [ -f "$BOUNCER_CLI" ]; then
|
|
93
|
+
RESULT=$(echo "$INPUT" | timeout "$BOUNCER_TIMEOUT" "$TSX_PATH" "$BOUNCER_CLI" 2>> "$BOUNCER_LOG" || echo '{"decision": "allow", "reason": "Bouncer timeout or error"}')
|
|
94
|
+
echo "$RESULT"
|
|
95
|
+
else
|
|
96
|
+
# Fallback: use inline Node.js for basic pattern matching
|
|
97
|
+
node --input-type=module -e "
|
|
98
|
+
const input = JSON.parse(process.argv[1]);
|
|
99
|
+
const toolName = input.tool_name || input.toolName || 'unknown';
|
|
100
|
+
const toolInput = input.input || input.toolInput || {};
|
|
101
|
+
|
|
102
|
+
// Quick allow for read-only operations
|
|
103
|
+
const readOnly = ['Read', 'Glob', 'Grep', 'Search', 'List', 'WebFetch', 'WebSearch'];
|
|
104
|
+
if (readOnly.includes(toolName)) {
|
|
105
|
+
console.log(JSON.stringify({ decision: 'allow', reason: 'Read-only operation' }));
|
|
106
|
+
process.exit(0);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Build operation string
|
|
110
|
+
let op = toolName + ': ';
|
|
111
|
+
if (toolName === 'Bash') op += toolInput.command || '';
|
|
112
|
+
else if (['Write', 'Edit'].includes(toolName)) op += toolInput.file_path || toolInput.filePath || '';
|
|
113
|
+
else op += JSON.stringify(toolInput);
|
|
114
|
+
|
|
115
|
+
// Critical threat patterns
|
|
116
|
+
const threats = [
|
|
117
|
+
[/rm\\s+-rf\\s+(\\/|~)(\\$|\\s)/, 'recursive delete of root/home'],
|
|
118
|
+
[/:\\(\\)\\{.*\\}/, 'fork bomb'],
|
|
119
|
+
[/dd\\s+if=\\/dev\\/zero\\s+of=\\/dev\\/sd/, 'disk overwrite'],
|
|
120
|
+
[/mkfs\\s+\\/dev\\/sd/, 'filesystem format'],
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
for (const [pattern, reason] of threats) {
|
|
124
|
+
if (pattern.test(op)) {
|
|
125
|
+
console.log(JSON.stringify({ decision: 'deny', reason: 'Critical threat: ' + reason }));
|
|
126
|
+
process.exit(0);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
console.log(JSON.stringify({ decision: 'allow', reason: 'Fallback: basic check passed' }));
|
|
131
|
+
" "$INPUT"
|
|
132
|
+
fi
|
|
133
|
+
`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function ensureDirectories() {
|
|
137
|
+
log('Step 1: Checking ~/.claude directory structure...', colors.bold);
|
|
138
|
+
|
|
139
|
+
for (const dir of [CLAUDE_DIR, CLAUDE_HOOKS_DIR, join(CLAUDE_DIR, 'logs')]) {
|
|
140
|
+
if (!existsSync(dir)) {
|
|
141
|
+
log(` Creating ${dir}`, colors.dim);
|
|
142
|
+
mkdirSync(dir, { recursive: true });
|
|
143
|
+
} else {
|
|
144
|
+
log(` ${dir} exists`, colors.green);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
log(' Done!\n', colors.green);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function installBouncerHook(forceYes, isInteractive) {
|
|
152
|
+
log('Step 2: Installing bouncer hook...', colors.bold);
|
|
153
|
+
|
|
154
|
+
const hookDest = join(CLAUDE_HOOKS_DIR, 'bouncer.sh');
|
|
155
|
+
const hookContent = generateBouncerHook(TSX_PATH, BOUNCER_CLI_PATH);
|
|
156
|
+
const hookExists = existsSync(hookDest);
|
|
157
|
+
|
|
158
|
+
if (hookExists) {
|
|
159
|
+
log(` Hook already exists at ${hookDest}`, colors.yellow);
|
|
160
|
+
let overwrite = forceYes;
|
|
161
|
+
|
|
162
|
+
if (!forceYes && isInteractive) {
|
|
163
|
+
const answer = await prompt(' Overwrite existing hook? [y/N]: ');
|
|
164
|
+
overwrite = answer === 'y' || answer === 'yes';
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!overwrite) {
|
|
168
|
+
log(' Skipping hook installation', colors.dim);
|
|
169
|
+
log(' Done!\n', colors.green);
|
|
170
|
+
return hookDest;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
writeFileSync(hookDest, hookContent);
|
|
175
|
+
chmodSync(hookDest, 0o755);
|
|
176
|
+
log(` ${hookExists ? 'Overwrote' : 'Installed'} ${hookDest}`, colors.green);
|
|
177
|
+
log(' Done!\n', colors.green);
|
|
178
|
+
return hookDest;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function loadSettings() {
|
|
182
|
+
if (!existsSync(CLAUDE_SETTINGS_FILE)) {
|
|
183
|
+
return {};
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
const content = readFileSync(CLAUDE_SETTINGS_FILE, 'utf-8');
|
|
187
|
+
const settings = JSON.parse(content);
|
|
188
|
+
log(` Loaded existing settings from ${CLAUDE_SETTINGS_FILE}`, colors.dim);
|
|
189
|
+
return settings;
|
|
190
|
+
} catch (err) {
|
|
191
|
+
log(` Warning: Could not parse existing settings.json: ${err.message}`, colors.yellow);
|
|
192
|
+
log(' Will create new settings file', colors.dim);
|
|
193
|
+
return {};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async function configureSettings(hookDest, forceYes, isInteractive) {
|
|
198
|
+
log('Step 3: Configuring settings.json...', colors.bold);
|
|
199
|
+
|
|
200
|
+
const settings = loadSettings();
|
|
201
|
+
if (!settings.hooks) {
|
|
202
|
+
settings.hooks = {};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const bouncerHook = { type: 'command', command: hookDest, timeout: 10000 };
|
|
206
|
+
const bouncerHookConfig = [
|
|
207
|
+
{ matcher: 'Bash', hooks: [bouncerHook] },
|
|
208
|
+
{ matcher: 'Write', hooks: [bouncerHook] },
|
|
209
|
+
{ matcher: 'Edit', hooks: [bouncerHook] }
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
const existingPreToolUse = settings.hooks.PreToolUse;
|
|
213
|
+
|
|
214
|
+
if (existingPreToolUse) {
|
|
215
|
+
log(' Existing PreToolUse hook configuration found:', colors.yellow);
|
|
216
|
+
log(` ${JSON.stringify(existingPreToolUse, null, 2).split('\n').join('\n ')}`, colors.dim);
|
|
217
|
+
|
|
218
|
+
let update = forceYes;
|
|
219
|
+
if (!forceYes && isInteractive) {
|
|
220
|
+
const answer = await prompt(' Update PreToolUse hook to use Mstro bouncer? [y/N]: ');
|
|
221
|
+
update = answer === 'y' || answer === 'yes';
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (!update) {
|
|
225
|
+
log(' Keeping existing PreToolUse configuration', colors.dim);
|
|
226
|
+
} else {
|
|
227
|
+
settings.hooks.PreToolUse = bouncerHookConfig;
|
|
228
|
+
log(' Updated PreToolUse hook configuration', colors.green);
|
|
229
|
+
}
|
|
230
|
+
} else {
|
|
231
|
+
settings.hooks.PreToolUse = bouncerHookConfig;
|
|
232
|
+
log(' Added PreToolUse hook configuration', colors.green);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const settingsContent = JSON.stringify(settings, null, 2);
|
|
236
|
+
const settingsExists = existsSync(CLAUDE_SETTINGS_FILE);
|
|
237
|
+
|
|
238
|
+
if (settingsExists) {
|
|
239
|
+
const currentContent = readFileSync(CLAUDE_SETTINGS_FILE, 'utf-8');
|
|
240
|
+
if (currentContent === settingsContent) {
|
|
241
|
+
log(' No changes needed to settings.json', colors.dim);
|
|
242
|
+
} else {
|
|
243
|
+
const backupPath = `${CLAUDE_SETTINGS_FILE}.backup.${Date.now()}`;
|
|
244
|
+
writeFileSync(backupPath, currentContent);
|
|
245
|
+
log(` Backed up existing settings to ${backupPath}`, colors.dim);
|
|
246
|
+
writeFileSync(CLAUDE_SETTINGS_FILE, settingsContent);
|
|
247
|
+
log(` Updated ${CLAUDE_SETTINGS_FILE}`, colors.green);
|
|
248
|
+
}
|
|
249
|
+
} else {
|
|
250
|
+
writeFileSync(CLAUDE_SETTINGS_FILE, settingsContent);
|
|
251
|
+
log(` Created ${CLAUDE_SETTINGS_FILE}`, colors.green);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
log(' Done!\n', colors.green);
|
|
255
|
+
return settingsContent;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function printSummary(hookDest, settingsContent) {
|
|
259
|
+
log('=== Configuration Complete ===\n', colors.bold + colors.green);
|
|
260
|
+
log('The following files have been configured:', colors.bold);
|
|
261
|
+
log(` ${CLAUDE_SETTINGS_FILE}`, colors.dim);
|
|
262
|
+
log(` ${hookDest}`, colors.dim);
|
|
263
|
+
log('');
|
|
264
|
+
log('Your settings.json now contains:', colors.bold);
|
|
265
|
+
log(`${settingsContent.split('\n').map(l => ` ${l}`).join('\n')}`, colors.dim);
|
|
266
|
+
log('');
|
|
267
|
+
log('How the bouncer works:', colors.bold);
|
|
268
|
+
log('');
|
|
269
|
+
log(' Mstro sessions (headless): Full 2-layer security', colors.green);
|
|
270
|
+
log(' Layer 1: Pattern matching (<5ms) - instant allow/deny for known operations', colors.dim);
|
|
271
|
+
log(' Layer 2: AI analysis (~200-500ms) - context-aware review of ambiguous operations', colors.dim);
|
|
272
|
+
log('');
|
|
273
|
+
log(' Claude Code terminal REPL (claude): 1-layer security', colors.yellow);
|
|
274
|
+
log(' Layer 1: Pattern matching only - blocks critical threats (fork bombs,', colors.dim);
|
|
275
|
+
log(' destructive commands), allows everything else', colors.dim);
|
|
276
|
+
log(' The AI analysis layer requires a running mstro server.', colors.dim);
|
|
277
|
+
log('');
|
|
278
|
+
log('To disable the bouncer hook, remove the PreToolUse entry from', colors.dim);
|
|
279
|
+
log(`${CLAUDE_SETTINGS_FILE}`, colors.dim);
|
|
280
|
+
log('');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async function main() {
|
|
284
|
+
log('\n=== Mstro Claude Configuration ===\n', colors.bold + colors.blue);
|
|
285
|
+
|
|
286
|
+
const isInteractive = process.stdin.isTTY;
|
|
287
|
+
const forceYes = process.argv.includes('--yes') || process.argv.includes('-y');
|
|
288
|
+
|
|
289
|
+
ensureDirectories();
|
|
290
|
+
const hookDest = await installBouncerHook(forceYes, isInteractive);
|
|
291
|
+
const settingsContent = await configureSettings(hookDest, forceYes, isInteractive);
|
|
292
|
+
printSummary(hookDest, settingsContent);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
main().catch((err) => {
|
|
296
|
+
log(`\nError: ${err.message}`, colors.red);
|
|
297
|
+
process.exit(1);
|
|
298
|
+
});
|