ropilot 0.1.2 → 0.1.4

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/README.md CHANGED
@@ -39,7 +39,7 @@ npm install -g ropilot
39
39
 
40
40
  **Or use HTTP directly:**
41
41
  ```
42
- https://ropilot.ai/ropilot/mcp/YOUR_API_KEY/message
42
+ https://ropilot.ai/edge/mcp/YOUR_API_KEY
43
43
  ```
44
44
 
45
45
  3. **Make sure Roblox Studio is running** with the Ropilot plugin installed.
package/lib/config.js CHANGED
@@ -14,7 +14,7 @@ const GLOBAL_CONFIG_FILE = join(GLOBAL_CONFIG_DIR, 'config.json');
14
14
  const PROJECT_CONFIG_FILE = '.ropilot.json';
15
15
 
16
16
  // Edge server base URL
17
- export const EDGE_URL = 'https://ropilot.ai/ropilot';
17
+ export const EDGE_URL = 'https://ropilot.ai/edge';
18
18
 
19
19
  // Default config
20
20
  const DEFAULT_CONFIG = {
package/lib/proxy.js CHANGED
@@ -6,7 +6,14 @@
6
6
  */
7
7
 
8
8
  import { createInterface } from 'readline';
9
- import { getApiKey, EDGE_URL } from './config.js';
9
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
10
+ import { join } from 'path';
11
+ import { homedir } from 'os';
12
+ import { getApiKey, EDGE_URL, loadGlobalConfig, saveGlobalConfig } from './config.js';
13
+
14
+ // Plugin version check URL (served from ropilot.ai main server)
15
+ const PLUGIN_VERSION_URL = 'https://ropilot.ai/plugin/version';
16
+ const PLUGIN_DOWNLOAD_URL = 'https://ropilot.ai/plugin/StudioPlugin.lua';
10
17
 
11
18
  /**
12
19
  * Send JSON-RPC response to stdout
@@ -54,23 +61,11 @@ async function forwardToEdge(apiKey, request) {
54
61
  }
55
62
 
56
63
  /**
57
- * Handle MCP initialize request
64
+ * Handle MCP initialize request - forward to edge to get instructions
58
65
  */
59
- function handleInitialize(request) {
60
- return {
61
- jsonrpc: '2.0',
62
- id: request.id,
63
- result: {
64
- protocolVersion: '2024-11-05',
65
- capabilities: {
66
- tools: {}
67
- },
68
- serverInfo: {
69
- name: 'ropilot',
70
- version: '0.1.0'
71
- }
72
- }
73
- };
66
+ async function handleInitialize(apiKey, request) {
67
+ const edgeResponse = await forwardToEdge(apiKey, request);
68
+ return edgeResponse;
74
69
  }
75
70
 
76
71
  /**
@@ -97,7 +92,7 @@ async function processRequest(apiKey, request) {
97
92
 
98
93
  switch (method) {
99
94
  case 'initialize':
100
- return handleInitialize(request);
95
+ return await handleInitialize(apiKey, request);
101
96
 
102
97
  case 'initialized':
103
98
  // Notification, no response needed
@@ -115,6 +110,44 @@ async function processRequest(apiKey, request) {
115
110
  }
116
111
  }
117
112
 
113
+ /**
114
+ * Check for plugin updates (runs in background, writes to stderr)
115
+ */
116
+ async function checkPluginUpdate() {
117
+ try {
118
+ const response = await fetch(PLUGIN_VERSION_URL);
119
+ if (!response.ok) return;
120
+
121
+ const data = await response.json();
122
+ const latestVersion = data.version;
123
+ const downloadUrl = data.download_url || PLUGIN_DOWNLOAD_URL;
124
+
125
+ // Check if we've already notified about this version
126
+ const config = loadGlobalConfig();
127
+ const lastNotifiedVersion = config.lastPluginVersionNotified;
128
+
129
+ if (latestVersion && latestVersion !== lastNotifiedVersion) {
130
+ // Check if we have a stored plugin version
131
+ const storedVersion = config.pluginVersion;
132
+
133
+ if (!storedVersion || storedVersion !== latestVersion) {
134
+ // New version available - notify via stderr (doesn't interfere with MCP protocol)
135
+ console.error('');
136
+ console.error(`[Ropilot] Plugin update available: v${latestVersion}`);
137
+ console.error(`[Ropilot] Download: ${downloadUrl}`);
138
+ console.error(`[Ropilot] Install to Roblox Studio plugins folder and restart Studio.`);
139
+ console.error('');
140
+
141
+ // Save that we notified about this version
142
+ config.lastPluginVersionNotified = latestVersion;
143
+ saveGlobalConfig(config);
144
+ }
145
+ }
146
+ } catch (err) {
147
+ // Silently ignore update check errors - not critical
148
+ }
149
+ }
150
+
118
151
  /**
119
152
  * Run the stdio MCP server
120
153
  */
@@ -127,6 +160,9 @@ export async function serve() {
127
160
  process.exit(1);
128
161
  }
129
162
 
163
+ // Check for plugin updates in background (non-blocking)
164
+ checkPluginUpdate();
165
+
130
166
  // Read JSON-RPC messages from stdin
131
167
  let buffer = '';
132
168
  let contentLength = -1;
package/lib/setup.js CHANGED
@@ -5,11 +5,12 @@
5
5
  * - First-time setup
6
6
  * - Downloading system prompts
7
7
  * - Creating project configs
8
+ * - Installing Studio plugin
8
9
  */
9
10
 
10
- import { existsSync, writeFileSync, mkdirSync, readFileSync } from 'fs';
11
+ import { existsSync, writeFileSync, mkdirSync, readFileSync, readdirSync } from 'fs';
11
12
  import { join, dirname } from 'path';
12
- import { homedir } from 'os';
13
+ import { homedir, platform } from 'os';
13
14
  import { createInterface } from 'readline';
14
15
  import {
15
16
  loadGlobalConfig,
@@ -22,6 +23,65 @@ import {
22
23
  // Prompts storage directory
23
24
  const PROMPTS_DIR = join(homedir(), '.ropilot', 'prompts');
24
25
 
26
+ // Plugin URLs
27
+ const PLUGIN_VERSION_URL = 'https://ropilot.ai/plugin/version';
28
+ const PLUGIN_DOWNLOAD_URL = 'https://ropilot.ai/plugin/StudioPlugin.lua';
29
+
30
+ /**
31
+ * Check if running in WSL
32
+ */
33
+ function isWSL() {
34
+ try {
35
+ const release = readFileSync('/proc/version', 'utf-8').toLowerCase();
36
+ return release.includes('microsoft') || release.includes('wsl');
37
+ } catch {
38
+ return false;
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Get Windows username when running in WSL
44
+ */
45
+ function getWindowsUsername() {
46
+ try {
47
+ const { execSync } = require('child_process');
48
+ const username = execSync('cmd.exe /c echo %USERNAME%', { encoding: 'utf-8' }).trim();
49
+ if (username && !username.includes('%')) {
50
+ return username;
51
+ }
52
+ } catch {}
53
+ return null;
54
+ }
55
+
56
+ /**
57
+ * Get Roblox plugins folder path based on OS (with WSL detection)
58
+ */
59
+ function getPluginsFolder() {
60
+ const os = platform();
61
+
62
+ // Check for WSL first - need to use Windows path
63
+ if (os === 'linux' && isWSL()) {
64
+ const windowsUser = getWindowsUsername();
65
+ if (windowsUser) {
66
+ const pluginsPath = join('/mnt/c/Users', windowsUser, 'AppData', 'Local', 'Roblox', 'Plugins');
67
+ if (existsSync(dirname(pluginsPath)) || existsSync('/mnt/c/Users/' + windowsUser)) {
68
+ return pluginsPath;
69
+ }
70
+ }
71
+ console.log(' Warning: Could not detect Windows username for WSL plugin install');
72
+ return null;
73
+ }
74
+
75
+ if (os === 'win32') {
76
+ return join(process.env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local'), 'Roblox', 'Plugins');
77
+ } else if (os === 'darwin') {
78
+ return join(homedir(), 'Documents', 'Roblox', 'Plugins');
79
+ } else {
80
+ // Linux without WSL - Roblox doesn't officially support Linux
81
+ return null;
82
+ }
83
+ }
84
+
25
85
  /**
26
86
  * Prompt for user input
27
87
  */
@@ -39,6 +99,95 @@ function prompt(question) {
39
99
  });
40
100
  }
41
101
 
102
+ /**
103
+ * Install Studio plugin to Roblox plugins folder
104
+ */
105
+ async function installPlugin() {
106
+ console.log('Installing Studio plugin...');
107
+
108
+ try {
109
+ // Check plugin version
110
+ const versionResponse = await fetch(PLUGIN_VERSION_URL);
111
+ if (!versionResponse.ok) {
112
+ throw new Error(`Failed to fetch plugin version: ${versionResponse.status}`);
113
+ }
114
+ const versionInfo = await versionResponse.json();
115
+ console.log(` Plugin version: ${versionInfo.version}`);
116
+
117
+ // Download plugin
118
+ const pluginResponse = await fetch(PLUGIN_DOWNLOAD_URL);
119
+ if (!pluginResponse.ok) {
120
+ throw new Error(`Failed to download plugin: ${pluginResponse.status}`);
121
+ }
122
+ const pluginSource = await pluginResponse.text();
123
+
124
+ // Get plugins folder
125
+ const pluginsFolder = getPluginsFolder();
126
+
127
+ if (!pluginsFolder) {
128
+ console.log(' Could not determine Roblox plugins folder');
129
+ console.log(' Manual install: Download from https://ropilot.ai/plugin/StudioPlugin.lua');
130
+ return false;
131
+ }
132
+
133
+ // Create plugins folder if it doesn't exist
134
+ mkdirSync(pluginsFolder, { recursive: true });
135
+
136
+ // Write plugin file
137
+ const pluginPath = join(pluginsFolder, 'RopilotPlugin.lua');
138
+ writeFileSync(pluginPath, pluginSource);
139
+
140
+ console.log(` Installed to: ${pluginPath}`);
141
+
142
+ // Save version to config
143
+ const config = loadGlobalConfig();
144
+ config.pluginVersion = versionInfo.version;
145
+ saveGlobalConfig(config);
146
+
147
+ return true;
148
+ } catch (err) {
149
+ console.error(` Failed to install plugin: ${err.message}`);
150
+ console.log(' You can manually download from: https://ropilot.ai/plugin/StudioPlugin.lua');
151
+ return false;
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Create .mcp.json for Claude Code compatibility
157
+ */
158
+ function createMcpConfig() {
159
+ const mcpConfig = {
160
+ mcpServers: {
161
+ ropilot: {
162
+ command: "npx",
163
+ args: ["ropilot"]
164
+ }
165
+ }
166
+ };
167
+
168
+ const mcpPath = '.mcp.json';
169
+
170
+ if (existsSync(mcpPath)) {
171
+ // Merge with existing config
172
+ try {
173
+ const existing = JSON.parse(readFileSync(mcpPath, 'utf-8'));
174
+ if (!existing.mcpServers?.ropilot) {
175
+ existing.mcpServers = existing.mcpServers || {};
176
+ existing.mcpServers.ropilot = mcpConfig.mcpServers.ropilot;
177
+ writeFileSync(mcpPath, JSON.stringify(existing, null, 2));
178
+ console.log(` Updated: ${mcpPath} (added ropilot server)`);
179
+ } else {
180
+ console.log(` Exists: ${mcpPath} (ropilot already configured)`);
181
+ }
182
+ } catch (err) {
183
+ console.error(` Failed to update ${mcpPath}: ${err.message}`);
184
+ }
185
+ } else {
186
+ writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2));
187
+ console.log(` Created: ${mcpPath}`);
188
+ }
189
+ }
190
+
42
191
  /**
43
192
  * Download prompts from edge server
44
193
  */
@@ -209,34 +358,28 @@ export async function init(providedApiKey = null) {
209
358
  console.log('');
210
359
  }
211
360
 
361
+ // Install Studio plugin
362
+ console.log('');
363
+ await installPlugin();
364
+ console.log('');
365
+
212
366
  // Download prompts
213
367
  const pkg = await downloadPrompts(apiKey);
214
368
  console.log('');
215
369
 
216
- // Set up project prompts
370
+ // Set up project files
217
371
  console.log('Setting up project files...');
218
372
  setupProjectPrompts(pkg);
373
+ createMcpConfig();
219
374
  console.log('');
220
375
 
221
376
  // Done!
222
377
  console.log('Setup complete!');
223
378
  console.log('');
224
379
  console.log('Next steps:');
225
- console.log('1. Make sure Roblox Studio is running with the Ropilot plugin');
226
- console.log('2. Add Ropilot to your AI IDE:');
227
- console.log('');
228
- console.log(' For Claude Desktop, add to claude_desktop_config.json:');
229
- console.log(' {');
230
- console.log(' "mcpServers": {');
231
- console.log(' "ropilot": {');
232
- console.log(' "command": "npx",');
233
- console.log(' "args": ["ropilot"]');
234
- console.log(' }');
235
- console.log(' }');
236
- console.log(' }');
237
- console.log('');
238
- console.log(' Or use HTTP directly:');
239
- console.log(` ${EDGE_URL}/mcp/${apiKey}/message`);
380
+ console.log('1. Restart Roblox Studio to load the plugin');
381
+ console.log('2. In Studio: Plugins tab → Ropilot Settings → Enter your API key');
382
+ console.log('3. Run Claude Code in this directory - it will auto-detect .mcp.json');
240
383
  console.log('');
241
384
  }
242
385
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ropilot",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "AI-powered Roblox development assistant - MCP CLI",
5
5
  "author": "whut",
6
6
  "license": "MIT",