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,269 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
/**
|
|
4
|
+
* Agent Manager
|
|
5
|
+
*
|
|
6
|
+
* Handles agent discovery, installation, and availability checking.
|
|
7
|
+
* Implements pre-flight file copy approach for agent hot-loading.
|
|
8
|
+
*/
|
|
9
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync } from 'node:fs';
|
|
10
|
+
import { homedir } from 'node:os';
|
|
11
|
+
import { basename, dirname, join } from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
export class AgentManager {
|
|
14
|
+
bundledAgentsPath;
|
|
15
|
+
globalAgentsPath;
|
|
16
|
+
constructor() {
|
|
17
|
+
// Path to bundled agents in mstro installation
|
|
18
|
+
// In ES modules, we need to get __dirname equivalent using import.meta.url
|
|
19
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
const __dirname = dirname(__filename);
|
|
21
|
+
this.bundledAgentsPath = join(dirname(dirname(__dirname)), '.claude', 'agents');
|
|
22
|
+
// Global user agents directory
|
|
23
|
+
this.globalAgentsPath = join(homedir(), '.claude', 'agents');
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get path to project agents directory
|
|
27
|
+
*/
|
|
28
|
+
getProjectAgentsPath(workingDir) {
|
|
29
|
+
return join(workingDir, '.claude', 'agents');
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* List all available bundled agents
|
|
33
|
+
*/
|
|
34
|
+
listBundledAgents() {
|
|
35
|
+
if (!existsSync(this.bundledAgentsPath)) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
const files = readdirSync(this.bundledAgentsPath)
|
|
39
|
+
.filter(f => f.endsWith('.md') && f !== 'README.md');
|
|
40
|
+
return files.map(file => ({
|
|
41
|
+
name: basename(file, '.md'),
|
|
42
|
+
path: join(this.bundledAgentsPath, file),
|
|
43
|
+
location: 'bundled',
|
|
44
|
+
description: this.extractDescription(join(this.bundledAgentsPath, file))
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* List all globally installed agents
|
|
49
|
+
*/
|
|
50
|
+
listGlobalAgents() {
|
|
51
|
+
if (!existsSync(this.globalAgentsPath)) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
const files = readdirSync(this.globalAgentsPath)
|
|
55
|
+
.filter(f => f.endsWith('.md'));
|
|
56
|
+
return files.map(file => ({
|
|
57
|
+
name: basename(file, '.md'),
|
|
58
|
+
path: join(this.globalAgentsPath, file),
|
|
59
|
+
location: 'global',
|
|
60
|
+
description: this.extractDescription(join(this.globalAgentsPath, file))
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* List all project-level agents
|
|
65
|
+
*/
|
|
66
|
+
listProjectAgents(workingDir) {
|
|
67
|
+
const projectPath = this.getProjectAgentsPath(workingDir);
|
|
68
|
+
if (!existsSync(projectPath)) {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
const files = readdirSync(projectPath)
|
|
72
|
+
.filter(f => f.endsWith('.md'));
|
|
73
|
+
return files.map(file => ({
|
|
74
|
+
name: basename(file, '.md'),
|
|
75
|
+
path: join(projectPath, file),
|
|
76
|
+
location: 'project',
|
|
77
|
+
description: this.extractDescription(join(projectPath, file))
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Extract description from agent markdown file (first line after title)
|
|
82
|
+
*/
|
|
83
|
+
extractDescription(filePath) {
|
|
84
|
+
try {
|
|
85
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
86
|
+
const lines = content.split('\n').filter(l => l.trim());
|
|
87
|
+
// Find first non-header line
|
|
88
|
+
for (const line of lines) {
|
|
89
|
+
if (!line.startsWith('#')) {
|
|
90
|
+
return line.trim();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// Ignore errors
|
|
96
|
+
}
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Check if an agent is available (exists in project, global, or bundled)
|
|
101
|
+
*/
|
|
102
|
+
findAgent(agentName, workingDir) {
|
|
103
|
+
const agentFile = `${agentName}.md`;
|
|
104
|
+
// 1. Check project-level (if working dir provided)
|
|
105
|
+
if (workingDir) {
|
|
106
|
+
const projectPath = join(this.getProjectAgentsPath(workingDir), agentFile);
|
|
107
|
+
if (existsSync(projectPath)) {
|
|
108
|
+
return {
|
|
109
|
+
name: agentName,
|
|
110
|
+
path: projectPath,
|
|
111
|
+
location: 'project',
|
|
112
|
+
description: this.extractDescription(projectPath)
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// 2. Check global
|
|
117
|
+
const globalPath = join(this.globalAgentsPath, agentFile);
|
|
118
|
+
if (existsSync(globalPath)) {
|
|
119
|
+
return {
|
|
120
|
+
name: agentName,
|
|
121
|
+
path: globalPath,
|
|
122
|
+
location: 'global',
|
|
123
|
+
description: this.extractDescription(globalPath)
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// 3. Check bundled
|
|
127
|
+
const bundledPath = join(this.bundledAgentsPath, agentFile);
|
|
128
|
+
if (existsSync(bundledPath)) {
|
|
129
|
+
return {
|
|
130
|
+
name: agentName,
|
|
131
|
+
path: bundledPath,
|
|
132
|
+
location: 'bundled',
|
|
133
|
+
description: this.extractDescription(bundledPath)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Ensure an agent is available for use
|
|
140
|
+
* If not in project or global, copy from bundled to project
|
|
141
|
+
*/
|
|
142
|
+
async ensureAgentAvailable(agentName, workingDir) {
|
|
143
|
+
const agentFile = `${agentName}.md`;
|
|
144
|
+
const projectPath = join(this.getProjectAgentsPath(workingDir), agentFile);
|
|
145
|
+
const globalPath = join(this.globalAgentsPath, agentFile);
|
|
146
|
+
const bundledPath = join(this.bundledAgentsPath, agentFile);
|
|
147
|
+
// If already in project or global, we're done
|
|
148
|
+
if (existsSync(projectPath)) {
|
|
149
|
+
return {
|
|
150
|
+
name: agentName,
|
|
151
|
+
path: projectPath,
|
|
152
|
+
location: 'project',
|
|
153
|
+
description: this.extractDescription(projectPath)
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (existsSync(globalPath)) {
|
|
157
|
+
return {
|
|
158
|
+
name: agentName,
|
|
159
|
+
path: globalPath,
|
|
160
|
+
location: 'global',
|
|
161
|
+
description: this.extractDescription(globalPath)
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
// Check if bundled agent exists
|
|
165
|
+
if (!existsSync(bundledPath)) {
|
|
166
|
+
throw new Error(`Agent not found: ${agentName}\nNot available in bundled, global, or project agents.`);
|
|
167
|
+
}
|
|
168
|
+
// Copy bundled agent to project
|
|
169
|
+
const targetDir = dirname(projectPath);
|
|
170
|
+
mkdirSync(targetDir, { recursive: true });
|
|
171
|
+
copyFileSync(bundledPath, projectPath);
|
|
172
|
+
return {
|
|
173
|
+
name: agentName,
|
|
174
|
+
path: projectPath,
|
|
175
|
+
location: 'project',
|
|
176
|
+
description: this.extractDescription(projectPath)
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Install a specific bundled agent to global directory
|
|
181
|
+
*/
|
|
182
|
+
installAgentGlobally(agentName) {
|
|
183
|
+
const agentFile = `${agentName}.md`;
|
|
184
|
+
const bundledPath = join(this.bundledAgentsPath, agentFile);
|
|
185
|
+
const globalPath = join(this.globalAgentsPath, agentFile);
|
|
186
|
+
if (!existsSync(bundledPath)) {
|
|
187
|
+
throw new Error(`Bundled agent not found: ${agentName}`);
|
|
188
|
+
}
|
|
189
|
+
// Create global agents directory if needed
|
|
190
|
+
mkdirSync(this.globalAgentsPath, { recursive: true });
|
|
191
|
+
// Copy to global
|
|
192
|
+
copyFileSync(bundledPath, globalPath);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Install all bundled agents to global directory
|
|
196
|
+
*/
|
|
197
|
+
installAllAgentsGlobally() {
|
|
198
|
+
const bundled = this.listBundledAgents();
|
|
199
|
+
const installed = [];
|
|
200
|
+
mkdirSync(this.globalAgentsPath, { recursive: true });
|
|
201
|
+
for (const agent of bundled) {
|
|
202
|
+
const targetPath = join(this.globalAgentsPath, `${agent.name}.md`);
|
|
203
|
+
copyFileSync(agent.path, targetPath);
|
|
204
|
+
installed.push(agent.name);
|
|
205
|
+
}
|
|
206
|
+
return installed;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Install a specific bundled agent to project directory
|
|
210
|
+
*/
|
|
211
|
+
installAgentToProject(agentName, workingDir) {
|
|
212
|
+
const agentFile = `${agentName}.md`;
|
|
213
|
+
const bundledPath = join(this.bundledAgentsPath, agentFile);
|
|
214
|
+
const projectPath = join(this.getProjectAgentsPath(workingDir), agentFile);
|
|
215
|
+
if (!existsSync(bundledPath)) {
|
|
216
|
+
throw new Error(`Bundled agent not found: ${agentName}`);
|
|
217
|
+
}
|
|
218
|
+
// Create project agents directory if needed
|
|
219
|
+
const targetDir = dirname(projectPath);
|
|
220
|
+
mkdirSync(targetDir, { recursive: true });
|
|
221
|
+
// Copy to project
|
|
222
|
+
copyFileSync(bundledPath, projectPath);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Install all bundled agents to project directory
|
|
226
|
+
*/
|
|
227
|
+
installAllAgentsToProject(workingDir) {
|
|
228
|
+
const bundled = this.listBundledAgents();
|
|
229
|
+
const installed = [];
|
|
230
|
+
const projectAgentsPath = this.getProjectAgentsPath(workingDir);
|
|
231
|
+
mkdirSync(projectAgentsPath, { recursive: true });
|
|
232
|
+
for (const agent of bundled) {
|
|
233
|
+
const targetPath = join(projectAgentsPath, `${agent.name}.md`);
|
|
234
|
+
copyFileSync(agent.path, targetPath);
|
|
235
|
+
installed.push(agent.name);
|
|
236
|
+
}
|
|
237
|
+
return installed;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Extract agent names from a score object
|
|
241
|
+
*/
|
|
242
|
+
extractAgentNamesFromScore(score) {
|
|
243
|
+
if (!Array.isArray(score.movements))
|
|
244
|
+
return [];
|
|
245
|
+
const names = score.movements
|
|
246
|
+
.flatMap(m => Array.isArray(m.musicians) ? m.musicians : [])
|
|
247
|
+
.filter((m) => m.type === 'custom')
|
|
248
|
+
.map((m) => m.config?.agent || m.role)
|
|
249
|
+
.filter(Boolean);
|
|
250
|
+
return [...new Set(names)];
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Ensure all agents required by a score are available
|
|
254
|
+
*/
|
|
255
|
+
async ensureScoreAgentsAvailable(score, workingDir) {
|
|
256
|
+
const agentNames = this.extractAgentNamesFromScore(score);
|
|
257
|
+
const results = new Map();
|
|
258
|
+
for (const agentName of agentNames) {
|
|
259
|
+
const info = await this.ensureAgentAvailable(agentName, workingDir);
|
|
260
|
+
results.set(agentName, info);
|
|
261
|
+
}
|
|
262
|
+
return results;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Singleton instance
|
|
267
|
+
*/
|
|
268
|
+
export const agentManager = new AgentManager();
|
|
269
|
+
//# sourceMappingURL=agent-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-manager.js","sourceRoot":"","sources":["../../../server/utils/agent-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Root directory of the mstro package installation.
|
|
3
|
+
* This is the directory containing package.json.
|
|
4
|
+
*
|
|
5
|
+
* Structure: /path/to/mstro/server/utils/paths.ts
|
|
6
|
+
* So we go up 2 levels: utils -> server -> mstro root
|
|
7
|
+
*/
|
|
8
|
+
export declare const MSTRO_ROOT: string;
|
|
9
|
+
/**
|
|
10
|
+
* Path to the MCP bouncer server script
|
|
11
|
+
*/
|
|
12
|
+
export declare const MCP_SERVER_PATH: string;
|
|
13
|
+
/**
|
|
14
|
+
* Path to the MCP bouncer configuration template
|
|
15
|
+
*/
|
|
16
|
+
export declare const MCP_CONFIG_TEMPLATE_PATH: string;
|
|
17
|
+
/**
|
|
18
|
+
* Path to the hooks directory
|
|
19
|
+
*/
|
|
20
|
+
export declare const HOOKS_DIR: string;
|
|
21
|
+
/**
|
|
22
|
+
* Path to the bouncer hook script
|
|
23
|
+
*/
|
|
24
|
+
export declare const BOUNCER_HOOK: string;
|
|
25
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../server/utils/paths.ts"],"names":[],"mappings":"AAiBA;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,QAA8B,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,eAAe,QAA8C,CAAC;AAE3E;;GAEG;AACH,eAAO,MAAM,wBAAwB,QAAgD,CAAC;AAEtF;;GAEG;AACH,eAAO,MAAM,SAAS,QAA+B,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,YAAY,QAAmC,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
/**
|
|
4
|
+
* Path Utilities for Mstro Package
|
|
5
|
+
*
|
|
6
|
+
* Provides consistent path resolution for installed npm package.
|
|
7
|
+
* Works correctly whether running from source or installed globally.
|
|
8
|
+
*/
|
|
9
|
+
import { dirname, resolve } from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
// ES module equivalent of __dirname for this file
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
/**
|
|
15
|
+
* Root directory of the mstro package installation.
|
|
16
|
+
* This is the directory containing package.json.
|
|
17
|
+
*
|
|
18
|
+
* Structure: /path/to/mstro/server/utils/paths.ts
|
|
19
|
+
* So we go up 2 levels: utils -> server -> mstro root
|
|
20
|
+
*/
|
|
21
|
+
export const MSTRO_ROOT = resolve(__dirname, '../..');
|
|
22
|
+
/**
|
|
23
|
+
* Path to the MCP bouncer server script
|
|
24
|
+
*/
|
|
25
|
+
export const MCP_SERVER_PATH = resolve(MSTRO_ROOT, 'server/mcp/server.ts');
|
|
26
|
+
/**
|
|
27
|
+
* Path to the MCP bouncer configuration template
|
|
28
|
+
*/
|
|
29
|
+
export const MCP_CONFIG_TEMPLATE_PATH = resolve(MSTRO_ROOT, 'mstro-bouncer-mcp.json');
|
|
30
|
+
/**
|
|
31
|
+
* Path to the hooks directory
|
|
32
|
+
*/
|
|
33
|
+
export const HOOKS_DIR = resolve(MSTRO_ROOT, 'hooks');
|
|
34
|
+
/**
|
|
35
|
+
* Path to the bouncer hook script
|
|
36
|
+
*/
|
|
37
|
+
export const BOUNCER_HOOK = resolve(HOOKS_DIR, 'bouncer.sh');
|
|
38
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../server/utils/paths.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,gEAAgE;AAEhE;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,kDAAkD;AAClD,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAEtD;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC;AAE3E;;GAEG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAC,UAAU,EAAE,wBAAwB,CAAC,CAAC;AAEtF;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AAEtD;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find an available port starting from the default port
|
|
3
|
+
* Tries ports: 3001, 3002, 3003, ... up to 3010
|
|
4
|
+
*/
|
|
5
|
+
export declare function findAvailablePort(startPort?: number): Promise<number>;
|
|
6
|
+
/**
|
|
7
|
+
* Get the default port from environment or use 3001
|
|
8
|
+
*/
|
|
9
|
+
export declare function getDefaultPort(): number;
|
|
10
|
+
//# sourceMappingURL=port-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port-manager.d.ts","sourceRoot":"","sources":["../../../server/utils/port-manager.ts"],"names":[],"mappings":"AAuCA;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,SAAS,GAAE,MAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAYzF;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CASvC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
/**
|
|
4
|
+
* Port Manager - Finds available ports for Mstro instances
|
|
5
|
+
*
|
|
6
|
+
* Handles port discovery when multiple Mstro instances run on the same machine.
|
|
7
|
+
* Tries a range of ports starting from the default until an available one is found.
|
|
8
|
+
*/
|
|
9
|
+
import { createServer } from 'node:net';
|
|
10
|
+
const DEFAULT_PORT = 3001;
|
|
11
|
+
const MAX_PORT_ATTEMPTS = 10;
|
|
12
|
+
/**
|
|
13
|
+
* Check if a port is available
|
|
14
|
+
*/
|
|
15
|
+
async function isPortAvailable(port) {
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
const server = createServer();
|
|
18
|
+
server.once('error', (err) => {
|
|
19
|
+
if (err.code === 'EADDRINUSE') {
|
|
20
|
+
resolve(false);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
resolve(false);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
server.once('listening', () => {
|
|
27
|
+
server.close();
|
|
28
|
+
resolve(true);
|
|
29
|
+
});
|
|
30
|
+
server.listen(port);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Find an available port starting from the default port
|
|
35
|
+
* Tries ports: 3001, 3002, 3003, ... up to 3010
|
|
36
|
+
*/
|
|
37
|
+
export async function findAvailablePort(startPort = DEFAULT_PORT) {
|
|
38
|
+
for (let i = 0; i < MAX_PORT_ATTEMPTS; i++) {
|
|
39
|
+
const port = startPort + i;
|
|
40
|
+
const available = await isPortAvailable(port);
|
|
41
|
+
if (available) {
|
|
42
|
+
return port;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
throw new Error(`Could not find available port after ${MAX_PORT_ATTEMPTS} attempts (tried ${startPort}-${startPort + MAX_PORT_ATTEMPTS - 1})`);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get the default port from environment or use 3001
|
|
49
|
+
*/
|
|
50
|
+
export function getDefaultPort() {
|
|
51
|
+
const envPort = process.env.PORT;
|
|
52
|
+
if (envPort) {
|
|
53
|
+
const parsed = parseInt(envPort, 10);
|
|
54
|
+
if (!Number.isNaN(parsed) && parsed > 0 && parsed < 65536) {
|
|
55
|
+
return parsed;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return DEFAULT_PORT;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=port-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port-manager.js","sourceRoot":"","sources":["../../../server/utils/port-manager.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,gEAAgE;AAEhE;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAEvC,MAAM,YAAY,GAAG,IAAI,CAAA;AACzB,MAAM,iBAAiB,GAAG,EAAE,CAAA;AAE5B;;GAEG;AACH,KAAK,UAAU,eAAe,CAAC,IAAY;IACzC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,MAAM,GAAG,YAAY,EAAE,CAAA;QAE7B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAQ,EAAE,EAAE;YAChC,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBAC9B,OAAO,CAAC,KAAK,CAAC,CAAA;YAChB,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,KAAK,CAAC,CAAA;YAChB,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE;YAC5B,MAAM,CAAC,KAAK,EAAE,CAAA;YACd,OAAO,CAAC,IAAI,CAAC,CAAA;QACf,CAAC,CAAC,CAAA;QAEF,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACrB,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,YAAoB,YAAY;IACtE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,iBAAiB,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,SAAS,GAAG,CAAC,CAAA;QAC1B,MAAM,SAAS,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,CAAA;QAE7C,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,IAAI,CAAA;QACb,CAAC;IAEH,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,uCAAuC,iBAAiB,oBAAoB,SAAS,IAAI,SAAS,GAAG,iBAAiB,GAAG,CAAC,GAAG,CAAC,CAAA;AAChJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAA;IAChC,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;QACpC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,KAAK,EAAE,CAAC;YAC1D,OAAO,MAAM,CAAA;QACf,CAAC;IACH,CAAC;IACD,OAAO,YAAY,CAAA;AACrB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a port is available by trying to bind to it
|
|
3
|
+
* Uses '::' (IPv6 any) which also binds IPv4 on most systems,
|
|
4
|
+
* ensuring we detect servers listening on any interface.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isPortAvailable(port: number): Promise<boolean>;
|
|
7
|
+
/**
|
|
8
|
+
* Check multiple ports in parallel and return the first available one
|
|
9
|
+
*/
|
|
10
|
+
export declare function findFirstAvailablePort(ports: number[]): Promise<number | null>;
|
|
11
|
+
/**
|
|
12
|
+
* Find an available port starting from startPort
|
|
13
|
+
*/
|
|
14
|
+
export declare function findAvailablePort(startPort: number, maxTries?: number): Promise<number>;
|
|
15
|
+
/**
|
|
16
|
+
* Find an available port pair for frontend and backend
|
|
17
|
+
* Frontend = EVEN port (3000, 3002, 3004...)
|
|
18
|
+
* Backend = ODD port (3001, 3003, 3005...)
|
|
19
|
+
*
|
|
20
|
+
* Checks all candidate ports in parallel for fast detection.
|
|
21
|
+
*/
|
|
22
|
+
export declare function findAvailablePortPair(startPort?: number, maxPairs?: number): Promise<{
|
|
23
|
+
frontend: number;
|
|
24
|
+
backend: number;
|
|
25
|
+
}>;
|
|
26
|
+
//# sourceMappingURL=port.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port.d.ts","sourceRoot":"","sources":["../../../server/utils/port.ts"],"names":[],"mappings":"AAaA;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAkB9D;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMpF;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAQjG;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CAAC,SAAS,GAAE,MAAa,EAAE,QAAQ,GAAE,MAAW,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAgC3I"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
/**
|
|
4
|
+
* Port utilities for finding available ports
|
|
5
|
+
*
|
|
6
|
+
* Convention: Frontend uses EVEN ports (3000, 3002, 3004...)
|
|
7
|
+
* Backend uses ODD ports (3001, 3003, 3005...)
|
|
8
|
+
* This ensures they never compete for the same port.
|
|
9
|
+
*/
|
|
10
|
+
import { createServer } from 'node:net';
|
|
11
|
+
/**
|
|
12
|
+
* Check if a port is available by trying to bind to it
|
|
13
|
+
* Uses '::' (IPv6 any) which also binds IPv4 on most systems,
|
|
14
|
+
* ensuring we detect servers listening on any interface.
|
|
15
|
+
*/
|
|
16
|
+
export function isPortAvailable(port) {
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
const server = createServer();
|
|
19
|
+
server.once('error', () => {
|
|
20
|
+
server.close();
|
|
21
|
+
resolve(false); // Port is in use
|
|
22
|
+
});
|
|
23
|
+
server.once('listening', () => {
|
|
24
|
+
server.close();
|
|
25
|
+
resolve(true); // Port is available
|
|
26
|
+
});
|
|
27
|
+
// Use '::' to bind on all interfaces (IPv4 + IPv6)
|
|
28
|
+
// This ensures we detect servers on any interface
|
|
29
|
+
server.listen(port, '::');
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check multiple ports in parallel and return the first available one
|
|
34
|
+
*/
|
|
35
|
+
export async function findFirstAvailablePort(ports) {
|
|
36
|
+
const results = await Promise.all(ports.map(async (port) => ({ port, available: await isPortAvailable(port) })));
|
|
37
|
+
const available = results.find(r => r.available);
|
|
38
|
+
return available ? available.port : null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Find an available port starting from startPort
|
|
42
|
+
*/
|
|
43
|
+
export async function findAvailablePort(startPort, maxTries = 20) {
|
|
44
|
+
// Check all ports in parallel for speed
|
|
45
|
+
const ports = Array.from({ length: maxTries }, (_, i) => startPort + i);
|
|
46
|
+
const port = await findFirstAvailablePort(ports);
|
|
47
|
+
if (port !== null) {
|
|
48
|
+
return port;
|
|
49
|
+
}
|
|
50
|
+
throw new Error(`No available ports found between ${startPort} and ${startPort + maxTries}`);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Find an available port pair for frontend and backend
|
|
54
|
+
* Frontend = EVEN port (3000, 3002, 3004...)
|
|
55
|
+
* Backend = ODD port (3001, 3003, 3005...)
|
|
56
|
+
*
|
|
57
|
+
* Checks all candidate ports in parallel for fast detection.
|
|
58
|
+
*/
|
|
59
|
+
export async function findAvailablePortPair(startPort = 3000, maxPairs = 20) {
|
|
60
|
+
// Ensure startPort is even
|
|
61
|
+
const basePort = startPort % 2 === 0 ? startPort : startPort + 1;
|
|
62
|
+
// Generate all candidate pairs
|
|
63
|
+
const pairs = [];
|
|
64
|
+
for (let i = 0; i < maxPairs; i++) {
|
|
65
|
+
pairs.push({
|
|
66
|
+
frontend: basePort + (i * 2), // 3000, 3002, 3004...
|
|
67
|
+
backend: basePort + (i * 2) + 1 // 3001, 3003, 3005...
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// Check all ports in parallel (both frontend and backend ports)
|
|
71
|
+
const allPorts = pairs.flatMap(p => [p.frontend, p.backend]);
|
|
72
|
+
const results = await Promise.all(allPorts.map(async (port) => ({ port, available: await isPortAvailable(port) })));
|
|
73
|
+
// Build a set of available ports for O(1) lookup
|
|
74
|
+
const availablePorts = new Set(results.filter(r => r.available).map(r => r.port));
|
|
75
|
+
// Find first pair where both ports are available
|
|
76
|
+
for (const pair of pairs) {
|
|
77
|
+
if (availablePorts.has(pair.frontend) && availablePorts.has(pair.backend)) {
|
|
78
|
+
return pair;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
throw new Error(`No available port pairs found starting from ${startPort}`);
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=port.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port.js","sourceRoot":"","sources":["../../../server/utils/port.ts"],"names":[],"mappings":"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"}
|