@hypothesi/tauri-mcp-server 0.2.1 → 0.3.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.
@@ -2,19 +2,15 @@
2
2
  * Single source of truth for all MCP tool definitions
3
3
  * This file defines all available tools and their metadata
4
4
  */
5
- import { runTauriCommand, RunCommandSchema } from './manager/cli.js';
6
- import { readConfig, writeConfig, ReadConfigSchema, WriteConfigSchema } from './manager/config.js';
7
- import { listDevices, launchEmulator, ListDevicesSchema, LaunchEmulatorSchema } from './manager/mobile.js';
5
+ import { listDevices, ListDevicesSchema } from './manager/mobile.js';
8
6
  import { manageDriverSession, ManageDriverSessionSchema, } from './driver/session-manager.js';
9
7
  import { readLogs, ReadLogsSchema } from './monitor/logs.js';
10
- import { getDocs, GetDocsSchema } from './manager/docs.js';
11
- import { executeIPCCommand, getWindowInfo, manageIPCMonitoring, getIPCEvents, emitTestEvent, getBackendState, listWindows, ExecuteIPCCommandSchema, GetWindowInfoSchema, ManageIPCMonitoringSchema, GetIPCEventsSchema, EmitTestEventSchema, GetBackendStateSchema, ListWindowsSchema, } from './driver/plugin-commands.js';
12
- import { interact, screenshot, keyboard, waitFor, getStyles, executeJavaScript, focusElement, findElement, getConsoleLogs, InteractSchema, ScreenshotSchema, KeyboardSchema, WaitForSchema, GetStylesSchema, ExecuteJavaScriptSchema, FocusElementSchema, FindElementSchema, GetConsoleLogsSchema, } from './driver/webview-interactions.js';
8
+ import { executeIPCCommand, manageIPCMonitoring, getIPCEvents, emitTestEvent, getBackendState, listWindows, ExecuteIPCCommandSchema, ManageIPCMonitoringSchema, GetIPCEventsSchema, EmitTestEventSchema, GetBackendStateSchema, ListWindowsSchema, } from './driver/plugin-commands.js';
9
+ import { interact, screenshot, keyboard, waitFor, getStyles, executeJavaScript, findElement, InteractSchema, ScreenshotSchema, KeyboardSchema, WaitForSchema, GetStylesSchema, ExecuteJavaScriptSchema, FindElementSchema, } from './driver/webview-interactions.js';
13
10
  /**
14
11
  * Tool categories for organization
15
12
  */
16
13
  export const TOOL_CATEGORIES = {
17
- PROJECT_MANAGEMENT: 'Project Management',
18
14
  MOBILE_DEVELOPMENT: 'Mobile Development',
19
15
  UI_AUTOMATION: 'UI Automation & WebView Interaction',
20
16
  IPC_PLUGIN: 'IPC & Plugin Tools (via MCP Bridge)',
@@ -24,79 +20,6 @@ export const TOOL_CATEGORIES = {
24
20
  * This is the single source of truth for tool definitions
25
21
  */
26
22
  export const TOOLS = [
27
- // Project Management Tools
28
- {
29
- name: 'tauri_run_command',
30
- description: '[Tauri Desktop/Mobile Apps Only] Run Tauri CLI commands (dev, build, init, etc.). ' +
31
- 'Use ONLY for projects with a src-tauri/ directory and tauri.conf.json. ' +
32
- 'Do NOT use for regular web apps, Electron apps, or browser-based projects. ' +
33
- 'Check for tauri.conf.json before using this tool.',
34
- category: TOOL_CATEGORIES.PROJECT_MANAGEMENT,
35
- schema: RunCommandSchema,
36
- annotations: {
37
- title: 'Run Tauri CLI Command',
38
- readOnlyHint: false,
39
- destructiveHint: false,
40
- idempotentHint: false,
41
- openWorldHint: false,
42
- },
43
- handler: async (args) => {
44
- const parsed = RunCommandSchema.parse(args);
45
- return await runTauriCommand(parsed.command, parsed.cwd, parsed.args || [], parsed.timeout);
46
- },
47
- },
48
- {
49
- name: 'tauri_read_config',
50
- description: '[Tauri Desktop/Mobile Apps Only] Read tauri.conf.json or platform-specific configs. ' +
51
- 'Use ONLY for Tauri v2 projects (look for src-tauri/ directory). ' +
52
- 'For regular web apps or Electron, use standard file reading tools instead.',
53
- category: TOOL_CATEGORIES.PROJECT_MANAGEMENT,
54
- schema: ReadConfigSchema,
55
- annotations: {
56
- title: 'Read Tauri Config',
57
- readOnlyHint: true,
58
- openWorldHint: false,
59
- },
60
- handler: async (args) => {
61
- const parsed = ReadConfigSchema.parse(args);
62
- return await readConfig(parsed.projectPath, parsed.file);
63
- },
64
- },
65
- {
66
- name: 'tauri_write_config',
67
- description: '[Tauri Desktop/Mobile Apps Only] Modify tauri.conf.json with validation. ' +
68
- 'Use ONLY for Tauri v2 projects. Validates JSON structure before writing. ' +
69
- 'For other frameworks, use standard file editing tools.',
70
- category: TOOL_CATEGORIES.PROJECT_MANAGEMENT,
71
- schema: WriteConfigSchema,
72
- annotations: {
73
- title: 'Write Tauri Config',
74
- readOnlyHint: false,
75
- destructiveHint: true,
76
- openWorldHint: false,
77
- },
78
- handler: async (args) => {
79
- const parsed = WriteConfigSchema.parse(args);
80
- return await writeConfig(parsed.projectPath, parsed.file, parsed.content);
81
- },
82
- },
83
- {
84
- name: 'tauri_get_docs',
85
- description: '[Tauri Desktop/Mobile Apps Only] Fetch Tauri v2 documentation and API reference. ' +
86
- 'Use when working on Tauri projects and need framework-specific guidance. ' +
87
- 'Not useful for Electron, React Native, or web-only projects.',
88
- category: TOOL_CATEGORIES.PROJECT_MANAGEMENT,
89
- schema: GetDocsSchema,
90
- annotations: {
91
- title: 'Get Tauri Documentation',
92
- readOnlyHint: true,
93
- openWorldHint: true,
94
- },
95
- handler: async (args) => {
96
- const parsed = GetDocsSchema.parse(args);
97
- return await getDocs(parsed.projectPath);
98
- },
99
- },
100
23
  // Mobile Development Tools
101
24
  {
102
25
  name: 'tauri_list_devices',
@@ -115,28 +38,11 @@ export const TOOLS = [
115
38
  return `Android Devices:\n${devices.android.join('\n') || 'None'}\n\niOS Booted Simulators:\n${devices.ios.join('\n') || 'None'}`;
116
39
  },
117
40
  },
118
- {
119
- name: 'tauri_launch_emulator',
120
- description: '[Tauri Mobile Apps Only] Launch Android AVD or iOS Simulator for Tauri mobile testing. ' +
121
- 'Use when developing Tauri apps for mobile platforms. ' +
122
- 'Not applicable for desktop-only apps or web projects.',
123
- category: TOOL_CATEGORIES.MOBILE_DEVELOPMENT,
124
- schema: LaunchEmulatorSchema,
125
- annotations: {
126
- title: 'Launch Mobile Emulator',
127
- readOnlyHint: false,
128
- destructiveHint: false,
129
- openWorldHint: false,
130
- },
131
- handler: async (args) => {
132
- const parsed = LaunchEmulatorSchema.parse(args);
133
- return await launchEmulator(parsed.platform, parsed.name);
134
- },
135
- },
136
41
  // UI Automation Tools
137
42
  {
138
43
  name: 'tauri_driver_session',
139
44
  description: '[Tauri Apps Only] Start/stop automation session to connect to a RUNNING Tauri app. ' +
45
+ 'Use action "status" to check current connection state. ' +
140
46
  'REQUIRED before using other tauri_webview_* or tauri_plugin_* tools. ' +
141
47
  'Connects via WebSocket to the MCP Bridge plugin in the Tauri app. ' +
142
48
  'For browser automation, use Chrome DevTools MCP instead. ' +
@@ -177,47 +83,28 @@ export const TOOLS = [
177
83
  },
178
84
  },
179
85
  {
180
- name: 'tauri_driver_get_console_logs',
181
- description: '[Tauri Apps Only] Get JavaScript console logs from a running Tauri app. ' +
182
- 'Requires active tauri_driver_session. Use for debugging Tauri webview issues. ' +
183
- 'For browser console logs, use Chrome DevTools MCP instead.',
184
- category: TOOL_CATEGORIES.UI_AUTOMATION,
185
- schema: GetConsoleLogsSchema,
186
- annotations: {
187
- title: 'Get Tauri Console Logs',
188
- readOnlyHint: true,
189
- openWorldHint: false,
190
- },
191
- handler: async (args) => {
192
- const parsed = GetConsoleLogsSchema.parse(args);
193
- return await getConsoleLogs({
194
- filter: parsed.filter,
195
- since: parsed.since,
196
- windowId: parsed.windowId,
197
- });
198
- },
199
- },
200
- {
201
- name: 'tauri_read_platform_logs',
202
- description: '[Tauri Mobile Apps] Read Android logcat or iOS device logs for Tauri mobile apps. ' +
203
- 'Also reads system logs on desktop. ' +
204
- 'Use for debugging native/platform-level issues in Tauri apps.',
86
+ name: 'tauri_read_logs',
87
+ description: '[Tauri Apps Only] Read logs from various sources: "console" for webview JS logs, ' +
88
+ '"android" for logcat, "ios" for simulator logs, "system" for desktop logs. ' +
89
+ 'Requires active tauri_driver_session for console logs. ' +
90
+ 'Use for debugging Tauri app issues at any level.',
205
91
  category: TOOL_CATEGORIES.UI_AUTOMATION,
206
92
  schema: ReadLogsSchema,
207
93
  annotations: {
208
- title: 'Read Platform Logs',
94
+ title: 'Read Logs',
209
95
  readOnlyHint: true,
210
96
  openWorldHint: false,
211
97
  },
212
98
  handler: async (args) => {
213
99
  const parsed = ReadLogsSchema.parse(args);
214
- return await readLogs(parsed.source, parsed.lines, parsed.filter, parsed.since);
100
+ return await readLogs(parsed);
215
101
  },
216
102
  },
217
103
  // WebView Interaction Tools
218
104
  {
219
105
  name: 'tauri_webview_interact',
220
- description: '[Tauri Apps Only] Click, scroll, swipe, or perform gestures in a Tauri app webview. ' +
106
+ description: '[Tauri Apps Only] Click, scroll, swipe, focus, or perform gestures in a Tauri app webview. ' +
107
+ 'Supported actions: click, double-click, long-press, scroll, swipe, focus. ' +
221
108
  'Requires active tauri_driver_session. ' +
222
109
  'For browser interaction, use Chrome DevTools MCP instead.',
223
110
  category: TOOL_CATEGORIES.UI_AUTOMATION,
@@ -248,11 +135,18 @@ export const TOOLS = [
248
135
  },
249
136
  handler: async (args) => {
250
137
  const parsed = ScreenshotSchema.parse(args);
251
- return await screenshot({
138
+ const result = await screenshot({
252
139
  quality: parsed.quality,
253
140
  format: parsed.format,
254
141
  windowId: parsed.windowId,
142
+ filePath: parsed.filePath,
255
143
  });
144
+ // If saved to file, return text confirmation
145
+ if ('filePath' in result) {
146
+ return `Screenshot saved to: ${result.filePath}`;
147
+ }
148
+ // Return the content array directly for proper image handling
149
+ return result.content;
256
150
  },
257
151
  },
258
152
  {
@@ -352,27 +246,9 @@ export const TOOLS = [
352
246
  });
353
247
  },
354
248
  },
355
- {
356
- name: 'tauri_webview_focus_element',
357
- description: '[Tauri Apps Only] Focus a DOM element in a Tauri app\'s webview. ' +
358
- 'Requires active tauri_driver_session. ' +
359
- 'For browser focus, use Chrome DevTools MCP instead.',
360
- category: TOOL_CATEGORIES.UI_AUTOMATION,
361
- schema: FocusElementSchema,
362
- annotations: {
363
- title: 'Focus Element in Tauri',
364
- readOnlyHint: false,
365
- destructiveHint: false,
366
- openWorldHint: false,
367
- },
368
- handler: async (args) => {
369
- const parsed = FocusElementSchema.parse(args);
370
- return await focusElement({ selector: parsed.selector, windowId: parsed.windowId });
371
- },
372
- },
373
249
  // IPC & Plugin Tools
374
250
  {
375
- name: 'tauri_plugin_execute_ipc',
251
+ name: 'tauri_ipc_execute_command',
376
252
  description: '[Tauri Apps Only] Execute Tauri IPC commands (invoke Rust backend functions). ' +
377
253
  'Requires active tauri_driver_session. This is Tauri-specific IPC, not browser APIs. ' +
378
254
  'For Electron IPC or browser APIs, use appropriate tools for those frameworks.',
@@ -390,23 +266,7 @@ export const TOOLS = [
390
266
  },
391
267
  },
392
268
  {
393
- name: 'tauri_plugin_get_window_info',
394
- description: '[Tauri Apps Only] Get Tauri window information (size, position, state). ' +
395
- 'Requires active tauri_driver_session. ' +
396
- 'For browser window info, use Chrome DevTools MCP instead.',
397
- category: TOOL_CATEGORIES.IPC_PLUGIN,
398
- schema: GetWindowInfoSchema,
399
- annotations: {
400
- title: 'Get Tauri Window Info',
401
- readOnlyHint: true,
402
- openWorldHint: false,
403
- },
404
- handler: async () => {
405
- return await getWindowInfo();
406
- },
407
- },
408
- {
409
- name: 'tauri_plugin_ipc_monitor',
269
+ name: 'tauri_ipc_monitor',
410
270
  description: '[Tauri Apps Only] Monitor Tauri IPC calls between frontend and Rust backend. ' +
411
271
  'Requires active tauri_driver_session. Captures invoke() calls and responses. ' +
412
272
  'This is Tauri-specific; for browser network monitoring, use Chrome DevTools MCP.',
@@ -425,14 +285,14 @@ export const TOOLS = [
425
285
  },
426
286
  },
427
287
  {
428
- name: 'tauri_plugin_ipc_get_events',
429
- description: '[Tauri Apps Only] Get captured Tauri IPC events (requires ipc_monitor started). ' +
430
- 'Shows Tauri command invocations with arguments and responses. ' +
288
+ name: 'tauri_ipc_get_captured',
289
+ description: '[Tauri Apps Only] Get captured Tauri IPC traffic (requires ipc_monitor started). ' +
290
+ 'Shows captured commands (invoke calls) and events with arguments and responses. ' +
431
291
  'For browser network requests, use Chrome DevTools MCP instead.',
432
292
  category: TOOL_CATEGORIES.IPC_PLUGIN,
433
293
  schema: GetIPCEventsSchema,
434
294
  annotations: {
435
- title: 'Get Tauri IPC Events',
295
+ title: 'Get Captured IPC Traffic',
436
296
  readOnlyHint: true,
437
297
  openWorldHint: false,
438
298
  },
@@ -442,7 +302,7 @@ export const TOOLS = [
442
302
  },
443
303
  },
444
304
  {
445
- name: 'tauri_plugin_emit_event',
305
+ name: 'tauri_ipc_emit_event',
446
306
  description: '[Tauri Apps Only] Emit a Tauri event to test event handlers. ' +
447
307
  'Requires active tauri_driver_session. Events are Tauri-specific (not DOM events). ' +
448
308
  'For browser DOM events, use Chrome DevTools MCP instead.',
@@ -460,7 +320,7 @@ export const TOOLS = [
460
320
  },
461
321
  },
462
322
  {
463
- name: 'tauri_plugin_get_backend_state',
323
+ name: 'tauri_ipc_get_backend_state',
464
324
  description: '[Tauri Apps Only] Get Tauri backend state: app metadata, Tauri version, environment. ' +
465
325
  'Requires active tauri_driver_session. ' +
466
326
  'Use to verify you\'re connected to a Tauri app and get app info.',
@@ -478,7 +338,8 @@ export const TOOLS = [
478
338
  // Window Management Tools
479
339
  {
480
340
  name: 'tauri_list_windows',
481
- description: '[Tauri Apps Only] List all Tauri webview windows (labels, titles, URLs, state). ' +
341
+ description: '[Tauri Apps Only] List all Tauri webview windows with details including ' +
342
+ 'labels, titles, URLs, and state (focused, visible, isMain). ' +
482
343
  'Requires active tauri_driver_session. Use to discover windows before targeting them. ' +
483
344
  'For browser tabs/windows, use Chrome DevTools MCP instead.',
484
345
  category: TOOL_CATEGORIES.UI_AUTOMATION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypothesi/tauri-mcp-server",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "A Model Context Protocol server for Tauri v2 development",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,128 +0,0 @@
1
- import { z } from 'zod';
2
- import { execa } from 'execa';
3
- import fs from 'fs/promises';
4
- import path from 'path';
5
- export const RunCommandSchema = z.object({
6
- command: z.string().describe('Any Tauri CLI command (e.g., "init", "dev", "build", "android dev", "migrate", "plugin add", etc.)'),
7
- args: z.array(z.string()).optional().describe('Additional arguments to pass to the command'),
8
- cwd: z.string().describe('The project directory'),
9
- timeout: z.number().optional().describe('Command timeout in milliseconds (default: 180000)'),
10
- });
11
- /**
12
- * Get available Tauri commands by running 'tauri --help'
13
- */
14
- export async function listTauriCommands(cwd) {
15
- try {
16
- const packageJsonPath = path.join(cwd, 'package.json');
17
- const hasTauriScript = await fs
18
- .readFile(packageJsonPath, 'utf8')
19
- .then((content) => {
20
- try {
21
- const pkg = JSON.parse(content);
22
- return Boolean(pkg.scripts && pkg.scripts.tauri);
23
- }
24
- catch {
25
- return false;
26
- }
27
- })
28
- .catch(() => { return false; });
29
- let result;
30
- if (hasTauriScript) {
31
- result = await execa('npm', ['run', 'tauri', '--', '--help'], {
32
- cwd,
33
- timeout: 10000,
34
- });
35
- }
36
- else {
37
- result = await execa('tauri', ['--help'], {
38
- cwd,
39
- timeout: 10000,
40
- });
41
- }
42
- return result.stdout;
43
- }
44
- catch (error) {
45
- const message = error instanceof Error ? error.message : String(error);
46
- throw new Error(`Failed to get Tauri commands: ${message}`);
47
- }
48
- }
49
- export async function runTauriCommand(command, cwd, args = [], timeout = 180000) {
50
- try {
51
- // Split command if it contains spaces (e.g., "android dev" or "plugin add")
52
- const commandParts = command.split(' ');
53
- const allArgs = [...commandParts, ...args];
54
- const packageJsonPath = path.join(cwd, 'package.json');
55
- const hasTauriScript = await fs
56
- .readFile(packageJsonPath, 'utf8')
57
- .then((content) => {
58
- try {
59
- const pkg = JSON.parse(content);
60
- return Boolean(pkg.scripts && pkg.scripts.tauri);
61
- }
62
- catch {
63
- return false;
64
- }
65
- })
66
- .catch(() => { return false; });
67
- const isDevLikeCommand = commandParts[0] === 'dev' ||
68
- command.startsWith('android dev') ||
69
- command.startsWith('ios dev');
70
- const spawnTauri = () => {
71
- if (hasTauriScript) {
72
- return execa('npm', ['run', 'tauri', '--', ...allArgs], {
73
- cwd,
74
- timeout: isDevLikeCommand ? undefined : timeout,
75
- });
76
- }
77
- return execa('tauri', allArgs, {
78
- cwd,
79
- timeout: isDevLikeCommand ? undefined : timeout,
80
- });
81
- };
82
- const child = spawnTauri();
83
- if (isDevLikeCommand) {
84
- // For long-running dev commands, start the process and return once the
85
- // command appears healthy
86
- child.catch(() => { return; });
87
- await new Promise((resolve) => {
88
- setTimeout(resolve, 3000);
89
- });
90
- if (child.exitCode !== null) {
91
- if (child.exitCode === 0) {
92
- return 'Tauri dev command completed successfully.';
93
- }
94
- throw new Error('Tauri dev command exited unexpectedly. Check your project configuration and logs for details.');
95
- }
96
- const text = [
97
- 'Tauri dev command started. It may still be initializing; check your terminal',
98
- 'or devtools logs for live output. Use Ctrl+C in the appropriate terminal to',
99
- 'stop it.',
100
- ];
101
- return text.join(' ');
102
- }
103
- const result = await child;
104
- if (Array.isArray(result.stdout)) {
105
- return result.stdout.join('\n');
106
- }
107
- if (result.stdout instanceof Uint8Array) {
108
- return new TextDecoder().decode(result.stdout);
109
- }
110
- return result.stdout || 'Command completed successfully';
111
- }
112
- catch (error) {
113
- const message = error instanceof Error ? error.message : String(error), stderr = error && typeof error === 'object' && 'stderr' in error ? String(error.stderr) : '';
114
- // Provide more helpful error messages
115
- if (message.includes('Unknown command')) {
116
- throw new Error(`Unknown Tauri command: "${command}". Run 'tauri --help' to see available commands.\n${stderr}`);
117
- }
118
- if (message.includes('spawn tauri ENOENT')) {
119
- const msgParts = [
120
- 'Failed to run the Tauri CLI. Either add a "tauri" script to your',
121
- 'package.json (e.g., "tauri": "tauri") or install the global Tauri CLI',
122
- 'as described in the documentation, then try again.',
123
- ];
124
- throw new Error(msgParts.join(' '));
125
- }
126
- throw new Error(`Tauri command failed: ${message}${stderr ? `\n${stderr}` : ''}`);
127
- }
128
- }
@@ -1,142 +0,0 @@
1
- import { z } from 'zod';
2
- import fs from 'fs/promises';
3
- import path from 'path';
4
- // Tauri supports multiple configuration files
5
- const TAURI_CONFIG_FILES = [
6
- 'tauri.conf.json',
7
- 'tauri.conf.json5',
8
- 'Tauri.toml',
9
- // Platform-specific configs
10
- 'tauri.windows.conf.json',
11
- 'tauri.linux.conf.json',
12
- 'tauri.macos.conf.json',
13
- 'tauri.android.conf.json',
14
- 'tauri.ios.conf.json',
15
- // Build-specific configs
16
- 'tauri.conf.dev.json',
17
- 'tauri.conf.prod.json',
18
- // Project files
19
- 'Cargo.toml',
20
- 'package.json',
21
- // Mobile-specific files
22
- 'Info.plist',
23
- 'AndroidManifest.xml',
24
- ];
25
- export const ReadConfigSchema = z.object({
26
- projectPath: z.string(),
27
- file: z.enum(TAURI_CONFIG_FILES).describe('Tauri configuration file to read'),
28
- });
29
- export const WriteConfigSchema = z.object({
30
- projectPath: z.string(),
31
- file: z.enum(TAURI_CONFIG_FILES).describe('Tauri configuration file to write'),
32
- content: z.string().describe('The new content of the file'),
33
- });
34
- export async function readConfig(projectPath, file) {
35
- const filePath = path.join(projectPath, getRelativePath(file));
36
- try {
37
- const content = await fs.readFile(filePath, 'utf-8');
38
- return content;
39
- }
40
- catch (error) {
41
- throw new Error(`Failed to read ${file}: ${error}`);
42
- }
43
- }
44
- /**
45
- * List all available Tauri configuration files in the project
46
- */
47
- export async function listConfigFiles(projectPath) {
48
- const availableFiles = [];
49
- for (const file of TAURI_CONFIG_FILES) {
50
- const filePath = path.join(projectPath, getRelativePath(file));
51
- try {
52
- await fs.access(filePath);
53
- availableFiles.push(file);
54
- }
55
- catch (e) {
56
- // File doesn't exist, skip it
57
- }
58
- }
59
- return availableFiles;
60
- }
61
- export async function writeConfig(projectPath, file, content) {
62
- const filePath = path.join(projectPath, getRelativePath(file));
63
- // Validate JSON files
64
- if (file.endsWith('.json')) {
65
- try {
66
- JSON.parse(content);
67
- }
68
- catch (e) {
69
- throw new Error(`Invalid JSON content for ${file}`);
70
- }
71
- }
72
- // Validate JSON5 files
73
- if (file.endsWith('.json5')) {
74
- // JSON5 is a superset of JSON, basic validation
75
- // In production, you'd want to use a json5 parser
76
- try {
77
- // At minimum, check for basic syntax
78
- if (!content.trim()) {
79
- throw new Error(`Empty content for ${file}`);
80
- }
81
- }
82
- catch (e) {
83
- throw new Error(`Invalid content for ${file}`);
84
- }
85
- }
86
- // Validate TOML files
87
- if (file.endsWith('.toml')) {
88
- // Basic TOML validation - in production use a TOML parser
89
- if (!content.trim()) {
90
- throw new Error(`Empty content for ${file}`);
91
- }
92
- }
93
- // Validate XML files
94
- if (file.endsWith('.xml')) {
95
- // Basic XML validation
96
- if (!content.includes('<') || !content.includes('>')) {
97
- throw new Error(`Invalid XML content for ${file}`);
98
- }
99
- }
100
- // Validate plist files
101
- if (file.endsWith('.plist')) {
102
- // plist files are XML-based
103
- if (!content.includes('<?xml') || !content.includes('<plist')) {
104
- throw new Error(`Invalid plist content for ${file}`);
105
- }
106
- }
107
- try {
108
- await fs.writeFile(filePath, content, 'utf-8');
109
- return `Successfully wrote to ${file}`;
110
- }
111
- catch (error) {
112
- throw new Error(`Failed to write ${file}: ${error}`);
113
- }
114
- }
115
- function getRelativePath(file) {
116
- // Main Tauri config files
117
- if (file === 'tauri.conf.json' || file === 'tauri.conf.json5' || file === 'Tauri.toml') {
118
- return `src-tauri/${file}`;
119
- }
120
- // Platform-specific Tauri configs
121
- if (file.startsWith('tauri.') && (file.endsWith('.conf.json') || file.endsWith('.conf.json5'))) {
122
- return `src-tauri/${file}`;
123
- }
124
- // Cargo.toml
125
- if (file === 'Cargo.toml') {
126
- return 'src-tauri/Cargo.toml';
127
- }
128
- // iOS Info.plist
129
- if (file === 'Info.plist') {
130
- return 'src-tauri/gen/apple/Runner/Info.plist';
131
- }
132
- // Android manifest
133
- if (file === 'AndroidManifest.xml') {
134
- return 'src-tauri/gen/android/app/src/main/AndroidManifest.xml';
135
- }
136
- // Package.json is at root
137
- if (file === 'package.json') {
138
- return 'package.json';
139
- }
140
- // Default: assume it's relative to project root
141
- return file;
142
- }