@pschroee/redmine-mcp 0.3.1
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 +360 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +99 -0
- package/dist/redmine/client.d.ts +349 -0
- package/dist/redmine/client.js +458 -0
- package/dist/redmine/types.d.ts +489 -0
- package/dist/redmine/types.js +2 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.js +10 -0
- package/dist/tools/account.d.ts +3 -0
- package/dist/tools/account.js +10 -0
- package/dist/tools/admin.d.ts +3 -0
- package/dist/tools/admin.js +150 -0
- package/dist/tools/core.d.ts +3 -0
- package/dist/tools/core.js +242 -0
- package/dist/tools/enumerations.d.ts +3 -0
- package/dist/tools/enumerations.js +26 -0
- package/dist/tools/files.d.ts +3 -0
- package/dist/tools/files.js +70 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.js +59 -0
- package/dist/tools/issues.d.ts +3 -0
- package/dist/tools/issues.js +61 -0
- package/dist/tools/memberships.d.ts +3 -0
- package/dist/tools/memberships.js +66 -0
- package/dist/tools/metadata.d.ts +3 -0
- package/dist/tools/metadata.js +102 -0
- package/dist/tools/projects.d.ts +3 -0
- package/dist/tools/projects.js +49 -0
- package/dist/tools/relations.d.ts +3 -0
- package/dist/tools/relations.js +157 -0
- package/dist/tools/roles.d.ts +3 -0
- package/dist/tools/roles.js +22 -0
- package/dist/tools/search.d.ts +3 -0
- package/dist/tools/search.js +28 -0
- package/dist/tools/time.d.ts +3 -0
- package/dist/tools/time.js +75 -0
- package/dist/tools/wiki.d.ts +3 -0
- package/dist/tools/wiki.js +75 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
# Redmine MCP Server
|
|
2
|
+
|
|
3
|
+
A Model Context Protocol (MCP) server for interacting with Redmine project management.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @pschroee/redmine-mcp
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or use directly with npx:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @pschroee/redmine-mcp --url=https://your-redmine.com --api-key=your-api-key
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Configuration
|
|
18
|
+
|
|
19
|
+
### Required
|
|
20
|
+
|
|
21
|
+
- `--url` or `REDMINE_URL`: Your Redmine instance URL
|
|
22
|
+
- `--api-key` or `REDMINE_API_KEY`: Your Redmine API key
|
|
23
|
+
|
|
24
|
+
### Optional
|
|
25
|
+
|
|
26
|
+
- `--tools=<groups>`: Comma-separated list of tool groups to enable
|
|
27
|
+
- `--exclude=<groups>`: Comma-separated list of tool groups to exclude
|
|
28
|
+
- `--help`: Show usage information
|
|
29
|
+
|
|
30
|
+
## Tool Groups
|
|
31
|
+
|
|
32
|
+
| Group | Tools | Description |
|
|
33
|
+
|-------|-------|-------------|
|
|
34
|
+
| `core` | 14 | Issues & Projects (CRUD, watchers, archive) |
|
|
35
|
+
| `metadata` | 9 | Trackers, Statuses, Categories, Custom Fields, Queries |
|
|
36
|
+
| `wiki` | 5 | Wiki Pages (CRUD, versioning) |
|
|
37
|
+
| `files` | 5 | Attachments & Project Files |
|
|
38
|
+
| `relations` | 9 | Issue Relations & Versions |
|
|
39
|
+
| `search` | 1 | Global Search |
|
|
40
|
+
| `account` | 1 | Current User Account |
|
|
41
|
+
| `time` | 5 | Time Entry tracking (CRUD, filtering) |
|
|
42
|
+
| `enumerations` | 3 | Issue Priorities, Time Entry Activities, Document Categories |
|
|
43
|
+
| `memberships` | 5 | Project Memberships (CRUD) |
|
|
44
|
+
| `roles` | 2 | Roles listing and details |
|
|
45
|
+
| `admin` | 11 | Users & Groups management (admin only) |
|
|
46
|
+
|
|
47
|
+
**Total: 70 Tools**
|
|
48
|
+
|
|
49
|
+
## Usage Examples
|
|
50
|
+
|
|
51
|
+
### Load all tools (default)
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx @pschroee/redmine-mcp --url=https://redmine.example.com --api-key=abc123
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Load only specific groups
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npx @pschroee/redmine-mcp --tools=core,metadata
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Exclude specific groups
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx @pschroee/redmine-mcp --exclude=wiki,files
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Claude Configuration
|
|
70
|
+
|
|
71
|
+
### Quick Setup with Claude CLI
|
|
72
|
+
|
|
73
|
+
**macOS / Linux:**
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
claude mcp add redmine -s user -- npx -y @pschroee/redmine-mcp \
|
|
77
|
+
--url=https://your-redmine.com --api-key=your-api-key
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Windows:**
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
claude mcp add redmine -s user -- cmd /c npx -y @pschroee/redmine-mcp \
|
|
84
|
+
--url=https://your-redmine.com --api-key=your-api-key
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### With Tool Groups
|
|
88
|
+
|
|
89
|
+
**macOS / Linux:**
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
claude mcp add redmine -s user -- npx -y @pschroee/redmine-mcp \
|
|
93
|
+
--url=https://your-redmine.com --api-key=your-api-key \
|
|
94
|
+
--tools=core,metadata,search
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Windows:**
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
claude mcp add redmine -s user -- cmd /c npx -y @pschroee/redmine-mcp \
|
|
101
|
+
--url=https://your-redmine.com --api-key=your-api-key \
|
|
102
|
+
--tools=core,metadata,search
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Manual Configuration (Claude Desktop)
|
|
106
|
+
|
|
107
|
+
#### macOS / Linux
|
|
108
|
+
|
|
109
|
+
Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"mcpServers": {
|
|
114
|
+
"redmine": {
|
|
115
|
+
"command": "npx",
|
|
116
|
+
"args": [
|
|
117
|
+
"@pschroee/redmine-mcp",
|
|
118
|
+
"--url=https://your-redmine.com",
|
|
119
|
+
"--api-key=your-api-key"
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
#### Windows
|
|
127
|
+
|
|
128
|
+
Add to your Claude Desktop config (`%APPDATA%\Claude\claude_desktop_config.json`):
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"mcpServers": {
|
|
133
|
+
"redmine": {
|
|
134
|
+
"command": "cmd",
|
|
135
|
+
"args": [
|
|
136
|
+
"/c",
|
|
137
|
+
"npx",
|
|
138
|
+
"@pschroee/redmine-mcp",
|
|
139
|
+
"--url=https://your-redmine.com",
|
|
140
|
+
"--api-key=your-api-key"
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### With Tool Groups
|
|
148
|
+
|
|
149
|
+
**macOS / Linux:**
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"mcpServers": {
|
|
154
|
+
"redmine": {
|
|
155
|
+
"command": "npx",
|
|
156
|
+
"args": [
|
|
157
|
+
"@pschroee/redmine-mcp",
|
|
158
|
+
"--url=https://your-redmine.com",
|
|
159
|
+
"--api-key=your-api-key",
|
|
160
|
+
"--tools=core,metadata,search"
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Windows:**
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"mcpServers": {
|
|
172
|
+
"redmine": {
|
|
173
|
+
"command": "cmd",
|
|
174
|
+
"args": [
|
|
175
|
+
"/c",
|
|
176
|
+
"npx",
|
|
177
|
+
"@pschroee/redmine-mcp",
|
|
178
|
+
"--url=https://your-redmine.com",
|
|
179
|
+
"--api-key=your-api-key",
|
|
180
|
+
"--tools=core,metadata,search"
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Available Tools
|
|
188
|
+
|
|
189
|
+
### Core (Issues & Projects)
|
|
190
|
+
|
|
191
|
+
- `list_issues` - List issues with filters and sorting
|
|
192
|
+
- `get_issue` - Get issue details with optional includes
|
|
193
|
+
- `create_issue` - Create new issue
|
|
194
|
+
- `update_issue` - Update issue (including adding notes)
|
|
195
|
+
- `delete_issue` - Delete issue
|
|
196
|
+
- `add_issue_watcher` - Add watcher to issue
|
|
197
|
+
- `remove_issue_watcher` - Remove watcher from issue
|
|
198
|
+
- `list_projects` - List all projects
|
|
199
|
+
- `get_project` - Get project details
|
|
200
|
+
- `create_project` - Create new project
|
|
201
|
+
- `update_project` - Update project
|
|
202
|
+
- `delete_project` - Delete project
|
|
203
|
+
- `archive_project` - Archive project (Redmine 5.0+)
|
|
204
|
+
- `unarchive_project` - Unarchive project (Redmine 5.0+)
|
|
205
|
+
|
|
206
|
+
### Metadata
|
|
207
|
+
|
|
208
|
+
- `list_trackers` - List all trackers
|
|
209
|
+
- `list_issue_statuses` - List all issue statuses
|
|
210
|
+
- `list_issue_categories` - List categories for a project
|
|
211
|
+
- `get_issue_category` - Get category details
|
|
212
|
+
- `create_issue_category` - Create category
|
|
213
|
+
- `update_issue_category` - Update category
|
|
214
|
+
- `delete_issue_category` - Delete category
|
|
215
|
+
- `list_custom_fields` - List custom fields (admin)
|
|
216
|
+
- `list_queries` - List saved queries
|
|
217
|
+
|
|
218
|
+
### Wiki
|
|
219
|
+
|
|
220
|
+
- `list_wiki_pages` - List wiki pages in project
|
|
221
|
+
- `get_wiki_page` - Get wiki page content
|
|
222
|
+
- `create_wiki_page` - Create wiki page
|
|
223
|
+
- `update_wiki_page` - Update wiki page
|
|
224
|
+
- `delete_wiki_page` - Delete wiki page
|
|
225
|
+
|
|
226
|
+
### Files
|
|
227
|
+
|
|
228
|
+
- `get_attachment` - Get attachment metadata
|
|
229
|
+
- `delete_attachment` - Delete attachment
|
|
230
|
+
- `upload_file` - Upload file (returns token)
|
|
231
|
+
- `list_project_files` - List project files
|
|
232
|
+
- `upload_project_file` - Attach file to project
|
|
233
|
+
|
|
234
|
+
### Relations
|
|
235
|
+
|
|
236
|
+
- `list_issue_relations` - List relations for issue
|
|
237
|
+
- `get_relation` - Get relation details
|
|
238
|
+
- `create_issue_relation` - Create relation
|
|
239
|
+
- `delete_relation` - Delete relation
|
|
240
|
+
- `list_versions` - List project versions
|
|
241
|
+
- `get_version` - Get version details
|
|
242
|
+
- `create_version` - Create version
|
|
243
|
+
- `update_version` - Update version
|
|
244
|
+
- `delete_version` - Delete version
|
|
245
|
+
|
|
246
|
+
### Search
|
|
247
|
+
|
|
248
|
+
- `search` - Search across Redmine
|
|
249
|
+
|
|
250
|
+
### Account
|
|
251
|
+
|
|
252
|
+
- `get_my_account` - Get current user info
|
|
253
|
+
|
|
254
|
+
### Time Entries
|
|
255
|
+
|
|
256
|
+
- `list_time_entries` - List time entries with filters (project, user, date range)
|
|
257
|
+
- `get_time_entry` - Get time entry details
|
|
258
|
+
- `create_time_entry` - Log time on issue or project
|
|
259
|
+
- `update_time_entry` - Update time entry
|
|
260
|
+
- `delete_time_entry` - Delete time entry
|
|
261
|
+
|
|
262
|
+
### Enumerations
|
|
263
|
+
|
|
264
|
+
- `list_issue_priorities` - List all issue priorities
|
|
265
|
+
- `list_time_entry_activities` - List all time entry activities
|
|
266
|
+
- `list_document_categories` - List all document categories
|
|
267
|
+
|
|
268
|
+
### Memberships
|
|
269
|
+
|
|
270
|
+
- `list_project_memberships` - List project members
|
|
271
|
+
- `get_membership` - Get membership details
|
|
272
|
+
- `create_project_membership` - Add member to project
|
|
273
|
+
- `update_membership` - Update member roles
|
|
274
|
+
- `delete_membership` - Remove member from project
|
|
275
|
+
|
|
276
|
+
### Roles
|
|
277
|
+
|
|
278
|
+
- `list_roles` - List all roles
|
|
279
|
+
- `get_role` - Get role with permissions
|
|
280
|
+
|
|
281
|
+
### Admin (Users & Groups)
|
|
282
|
+
|
|
283
|
+
- `list_users` - List users with filters
|
|
284
|
+
- `get_user` - Get user details
|
|
285
|
+
- `create_user` - Create new user
|
|
286
|
+
- `update_user` - Update user
|
|
287
|
+
- `delete_user` - Delete user
|
|
288
|
+
- `list_groups` - List all groups
|
|
289
|
+
- `get_group` - Get group details
|
|
290
|
+
- `create_group` - Create group
|
|
291
|
+
- `delete_group` - Delete group
|
|
292
|
+
- `add_user_to_group` - Add user to group
|
|
293
|
+
- `remove_user_from_group` - Remove user from group
|
|
294
|
+
|
|
295
|
+
## Development
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
# Install dependencies
|
|
299
|
+
npm install
|
|
300
|
+
|
|
301
|
+
# Run linter
|
|
302
|
+
npm run lint
|
|
303
|
+
|
|
304
|
+
# Fix lint errors
|
|
305
|
+
npm run lint:fix
|
|
306
|
+
|
|
307
|
+
# Build
|
|
308
|
+
npm run build
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Testing
|
|
312
|
+
|
|
313
|
+
The project includes comprehensive integration tests that run against a real Redmine instance.
|
|
314
|
+
|
|
315
|
+
### Setup
|
|
316
|
+
|
|
317
|
+
1. Create a `.env` file in the project root:
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
REDMINE_URL=http://your-redmine-instance:port
|
|
321
|
+
REDMINE_API_KEY=your-api-key
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
2. Run the tests:
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
npm test
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Test Coverage
|
|
331
|
+
|
|
332
|
+
The test suite includes 195 tests across 12 test files:
|
|
333
|
+
|
|
334
|
+
| Test File | Tests | Description |
|
|
335
|
+
|-----------|-------|-------------|
|
|
336
|
+
| `account.test.ts` | 1 | Current user account |
|
|
337
|
+
| `core.test.ts` | 48 | Projects and Issues CRUD |
|
|
338
|
+
| `metadata.test.ts` | 14 | Trackers, Statuses, Categories |
|
|
339
|
+
| `relations.test.ts` | 24 | Versions and Issue Relations |
|
|
340
|
+
| `wiki.test.ts` | 14 | Wiki Pages CRUD |
|
|
341
|
+
| `files.test.ts` | 11 | File uploads and attachments |
|
|
342
|
+
| `search.test.ts` | 12 | Global search functionality |
|
|
343
|
+
| `enumerations.test.ts` | 4 | Issue priorities, activities, document categories |
|
|
344
|
+
| `time.test.ts` | 17 | Time entry CRUD operations |
|
|
345
|
+
| `roles.test.ts` | 4 | Roles listing and details |
|
|
346
|
+
| `admin.test.ts` | 33 | Users and Groups management |
|
|
347
|
+
| `memberships.test.ts` | 13 | Project membership operations |
|
|
348
|
+
|
|
349
|
+
Tests are run sequentially and share state. A global setup creates the test project with all required modules enabled, and a global teardown cleans up all test data.
|
|
350
|
+
|
|
351
|
+
## Getting your API Key
|
|
352
|
+
|
|
353
|
+
1. Log into your Redmine instance
|
|
354
|
+
2. Go to My Account (usually `/my/account`)
|
|
355
|
+
3. Find "API access key" in the sidebar
|
|
356
|
+
4. Click "Show" or generate a new key
|
|
357
|
+
|
|
358
|
+
## License
|
|
359
|
+
|
|
360
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { RedmineClient } from "./redmine/client.js";
|
|
4
|
+
import { createServer } from "./server.js";
|
|
5
|
+
import { resolveGroups, ALL_GROUPS } from "./tools/index.js";
|
|
6
|
+
function printHelp() {
|
|
7
|
+
console.log(`
|
|
8
|
+
Redmine MCP Server
|
|
9
|
+
|
|
10
|
+
Usage: redmine-mcp [options]
|
|
11
|
+
|
|
12
|
+
Options:
|
|
13
|
+
--url=<url> Redmine URL (or set REDMINE_URL)
|
|
14
|
+
--api-key=<key> API Key (or set REDMINE_API_KEY)
|
|
15
|
+
--tools=<groups> Comma-separated list of tool groups to enable
|
|
16
|
+
--exclude=<groups> Comma-separated list of tool groups to exclude
|
|
17
|
+
--help Show this help message
|
|
18
|
+
|
|
19
|
+
Tool Groups:
|
|
20
|
+
${ALL_GROUPS.join(", ")}
|
|
21
|
+
|
|
22
|
+
Examples:
|
|
23
|
+
redmine-mcp --url=https://redmine.example.com --api-key=abc123
|
|
24
|
+
redmine-mcp --tools=core,metadata
|
|
25
|
+
redmine-mcp --exclude=wiki,files
|
|
26
|
+
`);
|
|
27
|
+
}
|
|
28
|
+
function parseArgs() {
|
|
29
|
+
const args = process.argv.slice(2);
|
|
30
|
+
let url;
|
|
31
|
+
let apiKey;
|
|
32
|
+
let toolsArg;
|
|
33
|
+
let excludeArg;
|
|
34
|
+
for (let i = 0; i < args.length; i++) {
|
|
35
|
+
const arg = args[i];
|
|
36
|
+
if (arg === "--help" || arg === "-h") {
|
|
37
|
+
printHelp();
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
if (arg === "--url" && args[i + 1]) {
|
|
41
|
+
url = args[++i];
|
|
42
|
+
}
|
|
43
|
+
else if (arg.startsWith("--url=")) {
|
|
44
|
+
url = arg.slice(6);
|
|
45
|
+
}
|
|
46
|
+
else if (arg === "--api-key" && args[i + 1]) {
|
|
47
|
+
apiKey = args[++i];
|
|
48
|
+
}
|
|
49
|
+
else if (arg.startsWith("--api-key=")) {
|
|
50
|
+
apiKey = arg.slice(10);
|
|
51
|
+
}
|
|
52
|
+
else if (arg === "--tools" && args[i + 1]) {
|
|
53
|
+
toolsArg = args[++i];
|
|
54
|
+
}
|
|
55
|
+
else if (arg.startsWith("--tools=")) {
|
|
56
|
+
toolsArg = arg.slice(8);
|
|
57
|
+
}
|
|
58
|
+
else if (arg === "--exclude" && args[i + 1]) {
|
|
59
|
+
excludeArg = args[++i];
|
|
60
|
+
}
|
|
61
|
+
else if (arg.startsWith("--exclude=")) {
|
|
62
|
+
excludeArg = arg.slice(10);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Fallback to environment variables
|
|
66
|
+
url = url || process.env.REDMINE_URL;
|
|
67
|
+
apiKey = apiKey || process.env.REDMINE_API_KEY;
|
|
68
|
+
if (!url) {
|
|
69
|
+
console.error("Error: Redmine URL required. Use --url or set REDMINE_URL");
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
if (!apiKey) {
|
|
73
|
+
console.error("Error: API key required. Use --api-key or set REDMINE_API_KEY");
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
// Parse tool groups
|
|
77
|
+
const include = toolsArg ? toolsArg.split(",").map((s) => s.trim()).filter(Boolean) : undefined;
|
|
78
|
+
const exclude = excludeArg ? excludeArg.split(",").map((s) => s.trim()).filter(Boolean) : undefined;
|
|
79
|
+
let toolGroups;
|
|
80
|
+
try {
|
|
81
|
+
toolGroups = resolveGroups(include, exclude);
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
return { url, apiKey, toolGroups };
|
|
88
|
+
}
|
|
89
|
+
async function main() {
|
|
90
|
+
const config = parseArgs();
|
|
91
|
+
const client = new RedmineClient(config.url, config.apiKey);
|
|
92
|
+
const server = createServer(client, config.toolGroups);
|
|
93
|
+
const transport = new StdioServerTransport();
|
|
94
|
+
await server.connect(transport);
|
|
95
|
+
}
|
|
96
|
+
main().catch((error) => {
|
|
97
|
+
console.error("Fatal error:", error);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
});
|