flutter-dev-mcp 1.0.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.
- package/LICENSE +21 -0
- package/README.md +239 -0
- package/dist/flutter-analyze.d.ts +16 -0
- package/dist/flutter-analyze.js +95 -0
- package/dist/flutter-analyze.js.map +1 -0
- package/dist/flutter-commands.d.ts +10 -0
- package/dist/flutter-commands.js +53 -0
- package/dist/flutter-commands.js.map +1 -0
- package/dist/flutter-devices.d.ts +9 -0
- package/dist/flutter-devices.js +48 -0
- package/dist/flutter-devices.js.map +1 -0
- package/dist/flutter-run.d.ts +22 -0
- package/dist/flutter-run.js +201 -0
- package/dist/flutter-run.js.map +1 -0
- package/dist/flutter-test.d.ts +19 -0
- package/dist/flutter-test.js +203 -0
- package/dist/flutter-test.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +346 -0
- package/dist/index.js.map +1 -0
- package/dist/validate.d.ts +22 -0
- package/dist/validate.js +63 -0
- package/dist/validate.js.map +1 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nick Clifford
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# flutter-dev-mcp
|
|
2
|
+
|
|
3
|
+
An MCP (Model Context Protocol) server that gives AI coding agents first-class Flutter development tools.
|
|
4
|
+
|
|
5
|
+
## Why?
|
|
6
|
+
|
|
7
|
+
Flutter CLI tools are designed for humans, not agents. `flutter test` dumps hundreds of lines of output that overwhelm context windows. `flutter run` requires interactive terminal access for hot reload. `flutter analyze` produces unstructured text. This MCP server wraps the Flutter CLI into structured, agent-friendly tools with sensible output limits.
|
|
8
|
+
|
|
9
|
+
Key design decisions:
|
|
10
|
+
- **Test results are two-phase**: `flutter_test` returns a compact summary of failures. `flutter_get_result` fetches full error details for specific tests. This prevents a single test run from blowing the context window.
|
|
11
|
+
- **All outputs are capped at 24KB** to stay within typical tool response limits.
|
|
12
|
+
- **`flutter run` is managed**: The server holds the process, exposes hot reload/restart/logs/kill as separate tools, so the agent doesn't need terminal access.
|
|
13
|
+
- **Inputs are sanitized**: All commands use array-based process spawning (no shell). Project paths are normalized and validated. Package names and device IDs are checked for flag injection.
|
|
14
|
+
|
|
15
|
+
## Tools at a glance
|
|
16
|
+
|
|
17
|
+
| Tool | Parameters | Description |
|
|
18
|
+
|------|-----------|-------------|
|
|
19
|
+
| `flutter_test` | project_dir, [test_path], [test_name] | Run tests and return a compact summary of failures only. Use `flutter_get_result` to drill into specific failures. |
|
|
20
|
+
| `flutter_get_result` | test_run_id, test_ids | Get full error details for specific test IDs from a previous `flutter_test` run. Output capped at 24KB. |
|
|
21
|
+
| `flutter_run` | project_dir, [device], [is_debug], [dont_detach] | Start a Flutter app on `device` (e.g. `macos`, `chrome`, emulator ID) in debug or release mode. By default detaches after the app starts and returns a `run_id`. |
|
|
22
|
+
| `flutter_hot_reload` | run_id | Hot reload a running app. |
|
|
23
|
+
| `flutter_hot_restart` | run_id | Hot restart a running app. |
|
|
24
|
+
| `flutter_kill` | run_id | Kill a running app. Graceful shutdown, force-kills after 5s. |
|
|
25
|
+
| `flutter_logs` | run_id | Get logs from a running app. Returns the most recent output, capped at 24KB. |
|
|
26
|
+
| `flutter_analyze` | project_dir | Run static analysis. Returns structured issues with severity, file, line, column, and rule name. |
|
|
27
|
+
| `flutter_devices` | [wireless] | List available devices (simulators, emulators, physical). Skips wireless scan by default. |
|
|
28
|
+
| `flutter_clean` | project_dir | Delete build artifacts. Useful when builds get into a bad state. |
|
|
29
|
+
| `flutter_pub_get` | project_dir | Resolve and download dependencies. |
|
|
30
|
+
| `flutter_pub_add` | project_dir, packages, [dev] | Add one or more packages. Supports `dev` dependencies. |
|
|
31
|
+
| `flutter_gen_l10n` | project_dir | Generate localization files from ARB files. |
|
|
32
|
+
| `flutter_build_runner` | project_dir, [delete_conflicting] | Run `build_runner` for code generation (freezed, json_serializable, drift, etc.). |
|
|
33
|
+
|
|
34
|
+
Parameters in `[brackets]` are optional.
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
Requires Node.js 18+ and Flutter SDK on your PATH.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install -g flutter-dev-mcp
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Or run directly with npx (recommended):
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx flutter-dev-mcp
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Configuration
|
|
51
|
+
|
|
52
|
+
### Claude Code (CLI)
|
|
53
|
+
|
|
54
|
+
Add to your MCP settings (`~/.claude.json` or project `.mcp.json`):
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"flutter-dev": {
|
|
60
|
+
"command": "npx",
|
|
61
|
+
"args": ["flutter-dev-mcp"]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Claude Desktop
|
|
68
|
+
|
|
69
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"mcpServers": {
|
|
74
|
+
"flutter-dev": {
|
|
75
|
+
"command": "npx",
|
|
76
|
+
"args": ["flutter-dev-mcp"]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Codex / Other agents
|
|
83
|
+
|
|
84
|
+
Any agent that supports MCP can use this server. Point it at the stdio transport:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npx flutter-dev-mcp
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The server communicates over stdin/stdout using the MCP JSON-RPC protocol.
|
|
91
|
+
|
|
92
|
+
## Tools
|
|
93
|
+
|
|
94
|
+
### Testing
|
|
95
|
+
|
|
96
|
+
#### `flutter_test`
|
|
97
|
+
|
|
98
|
+
Run tests and get a summary of failures.
|
|
99
|
+
|
|
100
|
+
| Parameter | Type | Required | Description |
|
|
101
|
+
|-----------|------|----------|-------------|
|
|
102
|
+
| `project_dir` | string | yes | Path to the Flutter project |
|
|
103
|
+
| `test_path` | string | no | Specific test file or directory |
|
|
104
|
+
| `test_name` | string | no | Filter by test name (plain string match) |
|
|
105
|
+
|
|
106
|
+
Returns a `test_run_id` and an array of failed tests with short error excerpts. Pass the `test_run_id` to `flutter_get_result` for full details.
|
|
107
|
+
|
|
108
|
+
#### `flutter_get_result`
|
|
109
|
+
|
|
110
|
+
Get full error output for specific test IDs from a previous run.
|
|
111
|
+
|
|
112
|
+
| Parameter | Type | Required | Description |
|
|
113
|
+
|-----------|------|----------|-------------|
|
|
114
|
+
| `test_run_id` | number | yes | From a previous `flutter_test` call |
|
|
115
|
+
| `test_ids` | number[] | yes | Test IDs to get details for |
|
|
116
|
+
|
|
117
|
+
Output is capped at 24KB total. If a single test exceeds that, its error is truncated. If multiple tests would exceed it, only tests that fit are returned.
|
|
118
|
+
|
|
119
|
+
### App lifecycle
|
|
120
|
+
|
|
121
|
+
#### `flutter_run`
|
|
122
|
+
|
|
123
|
+
Start a Flutter app and get a `run_id` for subsequent commands.
|
|
124
|
+
|
|
125
|
+
| Parameter | Type | Required | Default | Description |
|
|
126
|
+
|-----------|------|----------|---------|-------------|
|
|
127
|
+
| `project_dir` | string | yes | | Path to the Flutter project |
|
|
128
|
+
| `device` | string | no | `""` | Device ID (e.g. `chrome`, `macos`, emulator ID) |
|
|
129
|
+
| `is_debug` | boolean | no | `true` | Debug mode (true) or release mode (false) |
|
|
130
|
+
| `dont_detach` | boolean | no | `false` | Wait for app to exit instead of returning after start |
|
|
131
|
+
|
|
132
|
+
#### `flutter_hot_reload`
|
|
133
|
+
|
|
134
|
+
Trigger a hot reload on a running app.
|
|
135
|
+
|
|
136
|
+
| Parameter | Type | Required | Description |
|
|
137
|
+
|-----------|------|----------|-------------|
|
|
138
|
+
| `run_id` | number | yes | From a previous `flutter_run` call |
|
|
139
|
+
|
|
140
|
+
#### `flutter_hot_restart`
|
|
141
|
+
|
|
142
|
+
Trigger a hot restart on a running app.
|
|
143
|
+
|
|
144
|
+
| Parameter | Type | Required | Description |
|
|
145
|
+
|-----------|------|----------|-------------|
|
|
146
|
+
| `run_id` | number | yes | From a previous `flutter_run` call |
|
|
147
|
+
|
|
148
|
+
#### `flutter_kill`
|
|
149
|
+
|
|
150
|
+
Kill a running app. Sends `q` for graceful shutdown, force-kills after 5s.
|
|
151
|
+
|
|
152
|
+
| Parameter | Type | Required | Description |
|
|
153
|
+
|-----------|------|----------|-------------|
|
|
154
|
+
| `run_id` | number | yes | From a previous `flutter_run` call |
|
|
155
|
+
|
|
156
|
+
#### `flutter_logs`
|
|
157
|
+
|
|
158
|
+
Get logs from a running app. Returns the most recent output, capped at 24KB.
|
|
159
|
+
|
|
160
|
+
| Parameter | Type | Required | Description |
|
|
161
|
+
|-----------|------|----------|-------------|
|
|
162
|
+
| `run_id` | number | yes | From a previous `flutter_run` call |
|
|
163
|
+
|
|
164
|
+
### Analysis
|
|
165
|
+
|
|
166
|
+
#### `flutter_analyze`
|
|
167
|
+
|
|
168
|
+
Run static analysis. Returns structured issues with severity, file location, and lint rule name.
|
|
169
|
+
|
|
170
|
+
| Parameter | Type | Required | Description |
|
|
171
|
+
|-----------|------|----------|-------------|
|
|
172
|
+
| `project_dir` | string | yes | Path to the Flutter project |
|
|
173
|
+
|
|
174
|
+
#### `flutter_devices`
|
|
175
|
+
|
|
176
|
+
List available devices.
|
|
177
|
+
|
|
178
|
+
| Parameter | Type | Required | Default | Description |
|
|
179
|
+
|-----------|------|----------|---------|-------------|
|
|
180
|
+
| `wireless` | boolean | no | `false` | Include wireless devices (slower) |
|
|
181
|
+
|
|
182
|
+
### Dependencies & codegen
|
|
183
|
+
|
|
184
|
+
#### `flutter_pub_get`
|
|
185
|
+
|
|
186
|
+
Resolve and download dependencies.
|
|
187
|
+
|
|
188
|
+
| Parameter | Type | Required | Description |
|
|
189
|
+
|-----------|------|----------|-------------|
|
|
190
|
+
| `project_dir` | string | yes | Path to the Flutter project |
|
|
191
|
+
|
|
192
|
+
#### `flutter_pub_add`
|
|
193
|
+
|
|
194
|
+
Add packages to the project.
|
|
195
|
+
|
|
196
|
+
| Parameter | Type | Required | Default | Description |
|
|
197
|
+
|-----------|------|----------|---------|-------------|
|
|
198
|
+
| `project_dir` | string | yes | | Path to the Flutter project |
|
|
199
|
+
| `packages` | string[] | yes | | Package names (e.g. `["http", "provider"]`) |
|
|
200
|
+
| `dev` | boolean | no | `false` | Add as dev dependency |
|
|
201
|
+
|
|
202
|
+
#### `flutter_clean`
|
|
203
|
+
|
|
204
|
+
Delete build artifacts. Useful when builds get into a bad state.
|
|
205
|
+
|
|
206
|
+
| Parameter | Type | Required | Description |
|
|
207
|
+
|-----------|------|----------|-------------|
|
|
208
|
+
| `project_dir` | string | yes | Path to the Flutter project |
|
|
209
|
+
|
|
210
|
+
#### `flutter_gen_l10n`
|
|
211
|
+
|
|
212
|
+
Generate localization files from ARB files.
|
|
213
|
+
|
|
214
|
+
| Parameter | Type | Required | Description |
|
|
215
|
+
|-----------|------|----------|-------------|
|
|
216
|
+
| `project_dir` | string | yes | Path to the Flutter project |
|
|
217
|
+
|
|
218
|
+
#### `flutter_build_runner`
|
|
219
|
+
|
|
220
|
+
Run `dart run build_runner build` for code generation (freezed, json_serializable, drift, etc.).
|
|
221
|
+
|
|
222
|
+
| Parameter | Type | Required | Default | Description |
|
|
223
|
+
|-----------|------|----------|---------|-------------|
|
|
224
|
+
| `project_dir` | string | yes | | Path to the Flutter project |
|
|
225
|
+
| `delete_conflicting` | boolean | no | `true` | Delete conflicting outputs before building |
|
|
226
|
+
|
|
227
|
+
## Building from source
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
git clone <repo-url>
|
|
231
|
+
cd flutter-dev-mcp
|
|
232
|
+
npm install
|
|
233
|
+
npm run build
|
|
234
|
+
node dist/index.js
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## License
|
|
238
|
+
|
|
239
|
+
MIT
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
interface AnalyzeIssue {
|
|
2
|
+
severity: "error" | "warning" | "info";
|
|
3
|
+
message: string;
|
|
4
|
+
file: string;
|
|
5
|
+
line: number;
|
|
6
|
+
column: number;
|
|
7
|
+
rule: string;
|
|
8
|
+
}
|
|
9
|
+
export interface AnalyzeResult {
|
|
10
|
+
issues: AnalyzeIssue[];
|
|
11
|
+
error_count: number;
|
|
12
|
+
warning_count: number;
|
|
13
|
+
info_count: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function flutterAnalyze(projectDir: string): Promise<AnalyzeResult>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
export async function flutterAnalyze(projectDir) {
|
|
3
|
+
await runPubGet(projectDir);
|
|
4
|
+
const output = await runAnalyze(projectDir);
|
|
5
|
+
return parseAnalyzeOutput(output);
|
|
6
|
+
}
|
|
7
|
+
// Pattern: " info • message • file:line:col • rule_name"
|
|
8
|
+
const issuePattern = /^\s*(error|warning|info)\s+•\s+(.+?)\s+•\s+(.+?):(\d+):(\d+)\s+•\s+(\S+)\s*$/;
|
|
9
|
+
function parseAnalyzeOutput(output) {
|
|
10
|
+
const issues = [];
|
|
11
|
+
let errorCount = 0;
|
|
12
|
+
let warningCount = 0;
|
|
13
|
+
let infoCount = 0;
|
|
14
|
+
for (const line of output.split("\n")) {
|
|
15
|
+
const match = issuePattern.exec(line);
|
|
16
|
+
if (!match)
|
|
17
|
+
continue;
|
|
18
|
+
const severity = match[1];
|
|
19
|
+
issues.push({
|
|
20
|
+
severity,
|
|
21
|
+
message: match[2],
|
|
22
|
+
file: match[3],
|
|
23
|
+
line: parseInt(match[4], 10),
|
|
24
|
+
column: parseInt(match[5], 10),
|
|
25
|
+
rule: match[6],
|
|
26
|
+
});
|
|
27
|
+
if (severity === "error")
|
|
28
|
+
errorCount++;
|
|
29
|
+
else if (severity === "warning")
|
|
30
|
+
warningCount++;
|
|
31
|
+
else
|
|
32
|
+
infoCount++;
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
issues,
|
|
36
|
+
error_count: errorCount,
|
|
37
|
+
warning_count: warningCount,
|
|
38
|
+
info_count: infoCount,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function runPubGet(projectDir) {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
const proc = spawn("flutter", ["pub", "get"], {
|
|
44
|
+
cwd: projectDir,
|
|
45
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
46
|
+
});
|
|
47
|
+
let stderr = "";
|
|
48
|
+
proc.stderr.on("data", (data) => {
|
|
49
|
+
stderr += data.toString();
|
|
50
|
+
});
|
|
51
|
+
proc.on("close", (code) => {
|
|
52
|
+
if (code !== 0) {
|
|
53
|
+
reject(new Error(`flutter pub get failed with code ${code}: ${stderr}`));
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
resolve();
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
proc.on("error", (err) => {
|
|
60
|
+
reject(new Error(`Failed to start flutter pub get: ${err.message}`));
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function runAnalyze(projectDir) {
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
const proc = spawn("flutter", ["analyze", "--no-pub"], {
|
|
67
|
+
cwd: projectDir,
|
|
68
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
69
|
+
});
|
|
70
|
+
let stdout = "";
|
|
71
|
+
let stderr = "";
|
|
72
|
+
proc.stdout.on("data", (data) => {
|
|
73
|
+
stdout += data.toString();
|
|
74
|
+
});
|
|
75
|
+
proc.stderr.on("data", (data) => {
|
|
76
|
+
stderr += data.toString();
|
|
77
|
+
});
|
|
78
|
+
proc.on("close", (code) => {
|
|
79
|
+
// flutter analyze exits non-zero when issues are found — that's expected
|
|
80
|
+
if (stdout.length > 0) {
|
|
81
|
+
resolve(stdout);
|
|
82
|
+
}
|
|
83
|
+
else if (code !== 0 && stderr.length > 0) {
|
|
84
|
+
reject(new Error(`flutter analyze failed: ${stderr}`));
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
resolve(stdout);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
proc.on("error", (err) => {
|
|
91
|
+
reject(new Error(`Failed to start flutter analyze: ${err.message}`));
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=flutter-analyze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flutter-analyze.js","sourceRoot":"","sources":["../src/flutter-analyze.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface CommandResult {
|
|
2
|
+
success: boolean;
|
|
3
|
+
output: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function flutterClean(projectDir: string): Promise<CommandResult>;
|
|
6
|
+
export declare function flutterPubGet(projectDir: string): Promise<CommandResult>;
|
|
7
|
+
export declare function flutterPubAdd(projectDir: string, packages: string[], dev: boolean): Promise<CommandResult>;
|
|
8
|
+
export declare function flutterGenL10n(projectDir: string): Promise<CommandResult>;
|
|
9
|
+
export declare function flutterBuildRunner(projectDir: string, deleteConflicting: boolean): Promise<CommandResult>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
function runCommand(command, projectDir, args) {
|
|
3
|
+
return new Promise((resolve, reject) => {
|
|
4
|
+
const proc = spawn(command, args, {
|
|
5
|
+
cwd: projectDir,
|
|
6
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
7
|
+
});
|
|
8
|
+
let stdout = "";
|
|
9
|
+
let stderr = "";
|
|
10
|
+
proc.stdout.on("data", (data) => {
|
|
11
|
+
stdout += data.toString();
|
|
12
|
+
});
|
|
13
|
+
proc.stderr.on("data", (data) => {
|
|
14
|
+
stderr += data.toString();
|
|
15
|
+
});
|
|
16
|
+
proc.on("close", (code) => {
|
|
17
|
+
const output = (stdout + stderr).trim();
|
|
18
|
+
resolve({
|
|
19
|
+
success: code === 0,
|
|
20
|
+
output: output || `Exited with code ${code}`,
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
proc.on("error", (err) => {
|
|
24
|
+
reject(new Error(`Failed to run ${command} ${args.join(" ")}: ${err.message}`));
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function runFlutterCommand(projectDir, args) {
|
|
29
|
+
return runCommand("flutter", projectDir, args);
|
|
30
|
+
}
|
|
31
|
+
export async function flutterClean(projectDir) {
|
|
32
|
+
return runFlutterCommand(projectDir, ["clean"]);
|
|
33
|
+
}
|
|
34
|
+
export async function flutterPubGet(projectDir) {
|
|
35
|
+
return runFlutterCommand(projectDir, ["pub", "get"]);
|
|
36
|
+
}
|
|
37
|
+
export async function flutterPubAdd(projectDir, packages, dev) {
|
|
38
|
+
const args = ["pub", "add"];
|
|
39
|
+
if (dev)
|
|
40
|
+
args.push("--dev");
|
|
41
|
+
args.push(...packages);
|
|
42
|
+
return runFlutterCommand(projectDir, args);
|
|
43
|
+
}
|
|
44
|
+
export async function flutterGenL10n(projectDir) {
|
|
45
|
+
return runFlutterCommand(projectDir, ["gen-l10n"]);
|
|
46
|
+
}
|
|
47
|
+
export async function flutterBuildRunner(projectDir, deleteConflicting) {
|
|
48
|
+
const args = ["run", "build_runner", "build"];
|
|
49
|
+
if (deleteConflicting)
|
|
50
|
+
args.push("--delete-conflicting-outputs");
|
|
51
|
+
return runCommand("dart", projectDir, args);
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=flutter-commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flutter-commands.js","sourceRoot":"","sources":["../src/flutter-commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAO3C,SAAS,UAAU,CAAC,OAAe,EAAE,UAAkB,EAAE,IAAc;IACrE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE;YAChC,GAAG,EAAE,UAAU;YACf,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC;QAEH,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAEhB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;YACtC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;YACtC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACxB,MAAM,MAAM,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;YACxC,OAAO,CAAC;gBACN,OAAO,EAAE,IAAI,KAAK,CAAC;gBACnB,MAAM,EAAE,MAAM,IAAI,oBAAoB,IAAI,EAAE;aAC7C,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACvB,MAAM,CAAC,IAAI,KAAK,CAAC,iBAAiB,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,iBAAiB,CAAC,UAAkB,EAAE,IAAc;IAC3D,OAAO,UAAU,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,UAAkB;IACnD,OAAO,iBAAiB,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,UAAkB;IACpD,OAAO,iBAAiB,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,UAAkB,EAAE,QAAkB,EAAE,GAAY;IACtF,MAAM,IAAI,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC5B,IAAI,GAAG;QAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5B,IAAI,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;IACvB,OAAO,iBAAiB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,UAAkB;IACrD,OAAO,iBAAiB,CAAC,UAAU,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,UAAkB,EAAE,iBAA0B;IACrF,MAAM,IAAI,GAAG,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;IAC9C,IAAI,iBAAiB;QAAE,IAAI,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IACjE,OAAO,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
export async function flutterDevices(wireless) {
|
|
3
|
+
const output = await runDevices(wireless);
|
|
4
|
+
try {
|
|
5
|
+
const raw = JSON.parse(output);
|
|
6
|
+
return raw.map((d) => ({
|
|
7
|
+
name: d.name,
|
|
8
|
+
id: d.id,
|
|
9
|
+
targetPlatform: d.targetPlatform,
|
|
10
|
+
emulator: d.emulator,
|
|
11
|
+
sdk: d.sdk,
|
|
12
|
+
}));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
throw new Error(`Failed to parse flutter devices output: ${output.substring(0, 500)}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function runDevices(wireless) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const args = ["devices", "--machine"];
|
|
21
|
+
if (!wireless) {
|
|
22
|
+
args.push("--device-connection", "attached");
|
|
23
|
+
}
|
|
24
|
+
const proc = spawn("flutter", args, {
|
|
25
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
26
|
+
});
|
|
27
|
+
let stdout = "";
|
|
28
|
+
let stderr = "";
|
|
29
|
+
proc.stdout.on("data", (data) => {
|
|
30
|
+
stdout += data.toString();
|
|
31
|
+
});
|
|
32
|
+
proc.stderr.on("data", (data) => {
|
|
33
|
+
stderr += data.toString();
|
|
34
|
+
});
|
|
35
|
+
proc.on("close", (code) => {
|
|
36
|
+
if (code !== 0) {
|
|
37
|
+
reject(new Error(`flutter devices failed with code ${code}: ${stderr}`));
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
resolve(stdout);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
proc.on("error", (err) => {
|
|
44
|
+
reject(new Error(`Failed to start flutter devices: ${err.message}`));
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=flutter-devices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flutter-devices.js","sourceRoot":"","sources":["../src/flutter-devices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAU3C,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,QAAiB;IACpD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;IAE1C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAM3B,CAAC;QAEH,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACrB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,cAAc,EAAE,CAAC,CAAC,cAAc;YAChC,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,GAAG,EAAE,CAAC,CAAC,GAAG;SACX,CAAC,CAAC,CAAC;IACN,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,2CAA2C,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACzF,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,QAAiB;IACnC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,UAAU,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE;YAClC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC;QAEH,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAEhB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;YACtC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;YACtC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACxB,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,KAAK,CAAC,oCAAoC,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC;YAC3E,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,MAAM,CAAC,CAAC;YAClB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACvB,MAAM,CAAC,IAAI,KAAK,CAAC,oCAAoC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACvE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface FlutterRunResult {
|
|
2
|
+
run_id: number;
|
|
3
|
+
success: boolean;
|
|
4
|
+
error?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function flutterRun(projectDir: string, device: string, isDebug: boolean, dontDetach: boolean): Promise<FlutterRunResult>;
|
|
7
|
+
export declare function flutterHotReload(runId: number): {
|
|
8
|
+
success: boolean;
|
|
9
|
+
error?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function flutterHotRestart(runId: number): {
|
|
12
|
+
success: boolean;
|
|
13
|
+
error?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function flutterKill(runId: number): {
|
|
16
|
+
success: boolean;
|
|
17
|
+
error?: string;
|
|
18
|
+
};
|
|
19
|
+
export declare function flutterLogs(runId: number): {
|
|
20
|
+
logs: string;
|
|
21
|
+
error?: string;
|
|
22
|
+
};
|