flutter-skill-mcp 0.2.7 → 0.2.8

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/bin/cli.js CHANGED
@@ -3,65 +3,186 @@
3
3
  const { spawn, execSync } = require('child_process');
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
+ const https = require('https');
7
+ const os = require('os');
6
8
 
7
- // Find the dart directory
8
- const dartDir = path.join(__dirname, '..', 'dart');
9
- const serverScript = path.join(dartDir, 'bin', 'server.dart');
9
+ // Package info
10
+ const packageJson = require('../package.json');
11
+ const VERSION = packageJson.version;
10
12
 
11
- // Check if Dart is installed
12
- function checkDart() {
13
+ // Paths
14
+ const cacheDir = path.join(os.homedir(), '.flutter-skill');
15
+ const binDir = path.join(cacheDir, 'bin');
16
+
17
+ // Get platform-specific binary name
18
+ function getBinaryName() {
19
+ const platform = os.platform();
20
+ const arch = os.arch();
21
+
22
+ if (platform === 'darwin') {
23
+ return arch === 'arm64' ? 'flutter-skill-macos-arm64' : 'flutter-skill-macos-x64';
24
+ } else if (platform === 'linux') {
25
+ return 'flutter-skill-linux-x64';
26
+ } else if (platform === 'win32') {
27
+ return 'flutter-skill-windows-x64.exe';
28
+ }
29
+ return null;
30
+ }
31
+
32
+ // Get the local binary path
33
+ function getLocalBinaryPath() {
34
+ const binaryName = getBinaryName();
35
+ if (!binaryName) return null;
36
+ return path.join(binDir, `${binaryName}-v${VERSION}`);
37
+ }
38
+
39
+ // Download binary from GitHub releases
40
+ function downloadBinary(url, destPath) {
41
+ return new Promise((resolve, reject) => {
42
+ // Ensure directory exists
43
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
44
+
45
+ const file = fs.createWriteStream(destPath);
46
+
47
+ const request = (url) => {
48
+ https.get(url, (response) => {
49
+ // Handle redirects
50
+ if (response.statusCode === 302 || response.statusCode === 301) {
51
+ request(response.headers.location);
52
+ return;
53
+ }
54
+
55
+ if (response.statusCode !== 200) {
56
+ reject(new Error(`Failed to download: ${response.statusCode}`));
57
+ return;
58
+ }
59
+
60
+ response.pipe(file);
61
+ file.on('finish', () => {
62
+ file.close();
63
+ // Make executable
64
+ fs.chmodSync(destPath, 0o755);
65
+ resolve(destPath);
66
+ });
67
+ }).on('error', (err) => {
68
+ fs.unlink(destPath, () => {});
69
+ reject(err);
70
+ });
71
+ };
72
+
73
+ request(url);
74
+ });
75
+ }
76
+
77
+ // Try to use native binary, fallback to Dart
78
+ async function main() {
79
+ const binaryName = getBinaryName();
80
+ const localBinaryPath = getLocalBinaryPath();
81
+
82
+ // Try to use existing native binary
83
+ if (localBinaryPath && fs.existsSync(localBinaryPath)) {
84
+ runNativeBinary(localBinaryPath);
85
+ return;
86
+ }
87
+
88
+ // Try to download native binary
89
+ if (binaryName && localBinaryPath) {
90
+ const downloadUrl = `https://github.com/ai-dashboad/flutter-skill/releases/download/v${VERSION}/${binaryName}`;
91
+
92
+ try {
93
+ // Download in background, don't block startup for first time
94
+ // For now, just fall through to Dart
95
+ // Future: implement async download with progress
96
+ console.error(`[flutter-skill] Native binary not found, using Dart runtime`);
97
+ console.error(`[flutter-skill] To install native binary for faster startup:`);
98
+ console.error(`[flutter-skill] curl -L ${downloadUrl} -o ${localBinaryPath} && chmod +x ${localBinaryPath}`);
99
+ } catch (e) {
100
+ // Ignore download errors, fall back to Dart
101
+ }
102
+ }
103
+
104
+ // Fallback to Dart
105
+ runWithDart();
106
+ }
107
+
108
+ // Run using native binary
109
+ function runNativeBinary(binaryPath) {
110
+ const args = process.argv.slice(2);
111
+ // Default to 'server' command if no args
112
+ if (args.length === 0) {
113
+ args.push('server');
114
+ }
115
+
116
+ const server = spawn(binaryPath, args, {
117
+ stdio: 'inherit'
118
+ });
119
+
120
+ server.on('close', (code) => {
121
+ process.exit(code || 0);
122
+ });
123
+
124
+ process.on('SIGINT', () => server.kill('SIGINT'));
125
+ process.on('SIGTERM', () => server.kill('SIGTERM'));
126
+ }
127
+
128
+ // Run using Dart
129
+ function runWithDart() {
130
+ const dartDir = path.join(__dirname, '..', 'dart');
131
+ const serverScript = path.join(dartDir, 'bin', 'server.dart');
132
+
133
+ // Check if Dart is installed
13
134
  try {
14
135
  execSync('dart --version', { stdio: 'ignore' });
15
- return true;
16
136
  } catch (e) {
17
- return false;
137
+ console.error('Error: Dart SDK not found. Please install Flutter/Dart first.');
138
+ console.error(' https://docs.flutter.dev/get-started/install');
139
+ process.exit(1);
18
140
  }
19
- }
20
141
 
21
- // Check if Flutter is installed
22
- function checkFlutter() {
142
+ // Check if server script exists
143
+ if (!fs.existsSync(serverScript)) {
144
+ console.error('Error: Server script not found at:', serverScript);
145
+ process.exit(1);
146
+ }
147
+
148
+ // Get dependencies silently
23
149
  try {
24
- execSync('flutter --version', { stdio: 'ignore' });
25
- return true;
150
+ const pubCmd = checkFlutter() ? 'flutter' : 'dart';
151
+ execSync(`${pubCmd} pub get`, {
152
+ cwd: dartDir,
153
+ stdio: ['ignore', 'pipe', 'pipe']
154
+ });
26
155
  } catch (e) {
27
- return false;
156
+ // Ignore pub get errors
28
157
  }
29
- }
30
-
31
- if (!checkDart()) {
32
- console.error('Error: Dart SDK not found. Please install Flutter/Dart first.');
33
- console.error(' https://docs.flutter.dev/get-started/install');
34
- process.exit(1);
35
- }
36
158
 
37
- // Check if server script exists
38
- if (!fs.existsSync(serverScript)) {
39
- console.error('Error: Server script not found at:', serverScript);
40
- process.exit(1);
41
- }
159
+ // Start with Dart
160
+ const args = process.argv.slice(2);
161
+ if (args.length === 0) {
162
+ args.push('server');
163
+ }
42
164
 
43
- // Get dependencies silently (redirect to stderr to not interfere with MCP JSON-RPC)
44
- const pubCmd = checkFlutter() ? 'flutter' : 'dart';
45
- try {
46
- execSync(`${pubCmd} pub get`, {
165
+ const dartArgs = ['run', serverScript, ...args];
166
+ const server = spawn('dart', dartArgs, {
47
167
  cwd: dartDir,
48
- stdio: ['ignore', 'pipe', 'pipe'] // Silent - don't interfere with MCP stdin/stdout
168
+ stdio: 'inherit'
49
169
  });
50
- } catch (e) {
51
- // Log to stderr if pub get fails
52
- console.error('Warning: pub get failed, dependencies may be missing');
53
- }
54
170
 
55
- // Start the MCP server with proper stdio for JSON-RPC
56
- const server = spawn('dart', ['run', serverScript], {
57
- cwd: dartDir,
58
- stdio: 'inherit' // stdin/stdout/stderr passed through for MCP communication
59
- });
171
+ server.on('close', (code) => {
172
+ process.exit(code || 0);
173
+ });
60
174
 
61
- server.on('close', (code) => {
62
- process.exit(code || 0);
63
- });
175
+ process.on('SIGINT', () => server.kill('SIGINT'));
176
+ process.on('SIGTERM', () => server.kill('SIGTERM'));
177
+ }
178
+
179
+ function checkFlutter() {
180
+ try {
181
+ execSync('flutter --version', { stdio: 'ignore' });
182
+ return true;
183
+ } catch (e) {
184
+ return false;
185
+ }
186
+ }
64
187
 
65
- // Forward signals
66
- process.on('SIGINT', () => server.kill('SIGINT'));
67
- process.on('SIGTERM', () => server.kill('SIGTERM'));
188
+ main().catch(console.error);
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "flutter-skill-mcp",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "MCP Server for Flutter app automation - Give your AI Agent eyes and hands inside your Flutter app",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "flutter-skill-mcp": "./bin/cli.js"
8
8
  },
9
9
  "scripts": {
10
- "postinstall": "node scripts/check-dart.js"
10
+ "postinstall": "node scripts/postinstall.js"
11
11
  },
12
12
  "keywords": [
13
13
  "flutter",
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+
3
+ const https = require('https');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const os = require('os');
7
+
8
+ const packageJson = require('../package.json');
9
+ const VERSION = packageJson.version;
10
+
11
+ const cacheDir = path.join(os.homedir(), '.flutter-skill');
12
+ const binDir = path.join(cacheDir, 'bin');
13
+
14
+ function getBinaryName() {
15
+ const platform = os.platform();
16
+ const arch = os.arch();
17
+
18
+ if (platform === 'darwin') {
19
+ return arch === 'arm64' ? 'flutter-skill-macos-arm64' : 'flutter-skill-macos-x64';
20
+ } else if (platform === 'linux') {
21
+ return 'flutter-skill-linux-x64';
22
+ } else if (platform === 'win32') {
23
+ return 'flutter-skill-windows-x64.exe';
24
+ }
25
+ return null;
26
+ }
27
+
28
+ function downloadBinary(url, destPath) {
29
+ return new Promise((resolve, reject) => {
30
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
31
+
32
+ const file = fs.createWriteStream(destPath);
33
+
34
+ const request = (url) => {
35
+ https.get(url, (response) => {
36
+ if (response.statusCode === 302 || response.statusCode === 301) {
37
+ request(response.headers.location);
38
+ return;
39
+ }
40
+
41
+ if (response.statusCode !== 200) {
42
+ reject(new Error(`HTTP ${response.statusCode}`));
43
+ return;
44
+ }
45
+
46
+ const totalBytes = parseInt(response.headers['content-length'], 10);
47
+ let downloadedBytes = 0;
48
+
49
+ response.on('data', (chunk) => {
50
+ downloadedBytes += chunk.length;
51
+ if (totalBytes) {
52
+ const percent = Math.round((downloadedBytes / totalBytes) * 100);
53
+ process.stdout.write(`\r[flutter-skill] Downloading native binary... ${percent}%`);
54
+ }
55
+ });
56
+
57
+ response.pipe(file);
58
+ file.on('finish', () => {
59
+ file.close();
60
+ fs.chmodSync(destPath, 0o755);
61
+ console.log('\n[flutter-skill] Native binary installed successfully!');
62
+ resolve(destPath);
63
+ });
64
+ }).on('error', reject);
65
+ };
66
+
67
+ request(url);
68
+ });
69
+ }
70
+
71
+ async function main() {
72
+ const binaryName = getBinaryName();
73
+ if (!binaryName) {
74
+ console.log('[flutter-skill] No native binary available for this platform, using Dart runtime');
75
+ return;
76
+ }
77
+
78
+ const localPath = path.join(binDir, `${binaryName}-v${VERSION}`);
79
+
80
+ if (fs.existsSync(localPath)) {
81
+ console.log('[flutter-skill] Native binary already installed');
82
+ return;
83
+ }
84
+
85
+ const downloadUrl = `https://github.com/ai-dashboad/flutter-skill/releases/download/v${VERSION}/${binaryName}`;
86
+
87
+ console.log(`[flutter-skill] Installing native binary for faster startup...`);
88
+
89
+ try {
90
+ await downloadBinary(downloadUrl, localPath);
91
+ } catch (error) {
92
+ console.log(`[flutter-skill] Could not download native binary (${error.message}), will use Dart runtime`);
93
+ console.log('[flutter-skill] This is normal for new releases, Dart fallback works fine');
94
+ }
95
+ }
96
+
97
+ main().catch(() => {
98
+ // Silent fail - Dart fallback will work
99
+ });