@ebowwa/claude-code-config-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/.claude/CLAUDE.md +3 -0
- package/README.md +237 -0
- package/bun.lock +206 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1744 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +197 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +51 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/errors.d.ts +63 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +156 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/file.d.ts +32 -0
- package/dist/utils/file.d.ts.map +1 -0
- package/dist/utils/file.js +146 -0
- package/dist/utils/file.js.map +1 -0
- package/dist/utils/path.d.ts +59 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +146 -0
- package/dist/utils/path.js.map +1 -0
- package/lmdb.db +0 -0
- package/lmdb.db-lock +0 -0
- package/package.json +43 -0
- package/src/index.js +2171 -0
- package/src/index.ts +1981 -0
- package/src/types.js +53 -0
- package/src/types.ts +237 -0
- package/src/utils/errors.js +231 -0
- package/src/utils/errors.ts +210 -0
- package/src/utils/file.js +251 -0
- package/src/utils/file.ts +174 -0
- package/src/utils/path.js +169 -0
- package/src/utils/path.ts +173 -0
- package/test/test.js +136 -0
- package/test/test.ts +79 -0
- package/test/write-test.js +153 -0
- package/test/write-test.ts +102 -0
- package/tsconfig.json +21 -0
package/src/types.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConfigFileType = exports.HookFileSchema = exports.ClaudeMdSchema = exports.SettingsConfigSchema = exports.KeybindingsConfigSchema = exports.KeybindingSchema = void 0;
|
|
4
|
+
var zod_1 = require("zod");
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// Claude Code Configuration Types & Schemas
|
|
7
|
+
// ============================================================================
|
|
8
|
+
/**
|
|
9
|
+
* Keybinding schema
|
|
10
|
+
*/
|
|
11
|
+
exports.KeybindingSchema = zod_1.z.object({
|
|
12
|
+
key: zod_1.z.string().describe('Keyboard shortcut (e.g., "ctrl+s", "cmd+k")'),
|
|
13
|
+
command: zod_1.z.string().describe('Command to execute'),
|
|
14
|
+
when: zod_1.z.string().optional().describe('Context condition'),
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Keybindings configuration schema
|
|
18
|
+
*/
|
|
19
|
+
exports.KeybindingsConfigSchema = zod_1.z.object({
|
|
20
|
+
$schema: zod_1.z.string().optional(),
|
|
21
|
+
keybindings: zod_1.z.record(zod_1.z.string(), exports.KeybindingSchema),
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* Settings configuration schema (generic structure)
|
|
25
|
+
*/
|
|
26
|
+
exports.SettingsConfigSchema = zod_1.z.object({
|
|
27
|
+
$schema: zod_1.z.string().optional(),
|
|
28
|
+
// Additional settings fields will be validated based on actual Claude Code settings structure
|
|
29
|
+
});
|
|
30
|
+
/**
|
|
31
|
+
* CLAUDE.md content schema
|
|
32
|
+
*/
|
|
33
|
+
exports.ClaudeMdSchema = zod_1.z.object({
|
|
34
|
+
content: zod_1.z.string().min(1),
|
|
35
|
+
frontmatter: zod_1.z.record(zod_1.z.string(), zod_1.z.string()).optional(),
|
|
36
|
+
});
|
|
37
|
+
exports.HookFileSchema = zod_1.z.object({
|
|
38
|
+
name: zod_1.z.string(),
|
|
39
|
+
event: zod_1.z.string(),
|
|
40
|
+
enabled: zod_1.z.boolean(),
|
|
41
|
+
content: zod_1.z.string(),
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* Config file types
|
|
45
|
+
*/
|
|
46
|
+
var ConfigFileType;
|
|
47
|
+
(function (ConfigFileType) {
|
|
48
|
+
ConfigFileType["CLAUDE_MD_GLOBAL"] = "CLAUDE_MD_GLOBAL";
|
|
49
|
+
ConfigFileType["CLAUDE_MD_PROJECT"] = "CLAUDE_MD_PROJECT";
|
|
50
|
+
ConfigFileType["KEYBINDINGS"] = "KEYBINDINGS";
|
|
51
|
+
ConfigFileType["SETTINGS"] = "SETTINGS";
|
|
52
|
+
ConfigFileType["HOOKS"] = "HOOKS";
|
|
53
|
+
})(ConfigFileType || (exports.ConfigFileType = ConfigFileType = {}));
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Claude Code Configuration Types & Schemas
|
|
5
|
+
// ============================================================================
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Keybinding schema
|
|
9
|
+
*/
|
|
10
|
+
export const KeybindingSchema = z.object({
|
|
11
|
+
key: z.string().describe('Keyboard shortcut (e.g., "ctrl+s", "cmd+k")'),
|
|
12
|
+
command: z.string().describe('Command to execute'),
|
|
13
|
+
when: z.string().optional().describe('Context condition'),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export type Keybinding = z.infer<typeof KeybindingSchema>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Keybindings configuration schema
|
|
20
|
+
*/
|
|
21
|
+
export const KeybindingsConfigSchema = z.object({
|
|
22
|
+
$schema: z.string().optional(),
|
|
23
|
+
keybindings: z.record(z.string(), KeybindingSchema),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export type KeybindingsConfig = z.infer<typeof KeybindingsConfigSchema>;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Settings configuration schema (generic structure)
|
|
30
|
+
*/
|
|
31
|
+
export const SettingsConfigSchema = z.object({
|
|
32
|
+
$schema: z.string().optional(),
|
|
33
|
+
// Additional settings fields will be validated based on actual Claude Code settings structure
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export type SettingsConfig = z.infer<typeof SettingsConfigSchema>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* CLAUDE.md content schema
|
|
40
|
+
*/
|
|
41
|
+
export const ClaudeMdSchema = z.object({
|
|
42
|
+
content: z.string().min(1),
|
|
43
|
+
frontmatter: z.record(z.string(), z.string()).optional(),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export type ClaudeMd = z.infer<typeof ClaudeMdSchema>;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Hook file types
|
|
50
|
+
*/
|
|
51
|
+
export type HookEvent =
|
|
52
|
+
| 'SessionStart'
|
|
53
|
+
| 'SessionEnd'
|
|
54
|
+
| 'UserPromptSubmit'
|
|
55
|
+
| 'PreToolUse'
|
|
56
|
+
| 'PostToolUse'
|
|
57
|
+
| 'PreCompact'
|
|
58
|
+
| 'Stop'
|
|
59
|
+
| 'SubagentStop'
|
|
60
|
+
| 'Notification';
|
|
61
|
+
|
|
62
|
+
export const HookFileSchema = z.object({
|
|
63
|
+
name: z.string(),
|
|
64
|
+
event: z.string(),
|
|
65
|
+
enabled: z.boolean(),
|
|
66
|
+
content: z.string(),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export type HookFile = z.infer<typeof HookFileSchema>;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* File operation results
|
|
73
|
+
*/
|
|
74
|
+
export interface FileReadResult {
|
|
75
|
+
success: true;
|
|
76
|
+
data: string;
|
|
77
|
+
path: string;
|
|
78
|
+
size: number;
|
|
79
|
+
modified: Date;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface FileWriteResult {
|
|
83
|
+
success: true;
|
|
84
|
+
path: string;
|
|
85
|
+
backup?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface FileErrorResult {
|
|
89
|
+
success: false;
|
|
90
|
+
error: {
|
|
91
|
+
code: string;
|
|
92
|
+
message: string;
|
|
93
|
+
category: 'TRANSIENT' | 'PERMISSION' | 'VALIDATION' | 'SYSTEM' | 'UNKNOWN';
|
|
94
|
+
retryable: boolean;
|
|
95
|
+
userAction?: string;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export type FileOperationResult = FileReadResult | FileWriteResult | FileErrorResult;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Config file types
|
|
103
|
+
*/
|
|
104
|
+
export enum ConfigFileType {
|
|
105
|
+
CLAUDE_MD_GLOBAL = 'CLAUDE_MD_GLOBAL',
|
|
106
|
+
CLAUDE_MD_PROJECT = 'CLAUDE_MD_PROJECT',
|
|
107
|
+
KEYBINDINGS = 'KEYBINDINGS',
|
|
108
|
+
SETTINGS = 'SETTINGS',
|
|
109
|
+
HOOKS = 'HOOKS',
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Config file metadata
|
|
114
|
+
*/
|
|
115
|
+
export interface ConfigFileMeta {
|
|
116
|
+
type: ConfigFileType;
|
|
117
|
+
path: string;
|
|
118
|
+
exists: boolean;
|
|
119
|
+
size?: number;
|
|
120
|
+
modified?: Date;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Atomic write options
|
|
125
|
+
*/
|
|
126
|
+
export interface AtomicWriteOptions {
|
|
127
|
+
createBackup?: boolean;
|
|
128
|
+
backupDir?: string;
|
|
129
|
+
encoding?: BufferEncoding;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ============================================================================
|
|
133
|
+
// MCP Server Configuration Types
|
|
134
|
+
// ============================================================================
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* MCP transport types
|
|
138
|
+
*/
|
|
139
|
+
export type MCPTransport = 'stdio' | 'sse' | 'http' | 'ws' | 'websocket';
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Environment variable for MCP server
|
|
143
|
+
*/
|
|
144
|
+
export interface MCPEnvVar {
|
|
145
|
+
name: string;
|
|
146
|
+
value: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* MCP Server configuration
|
|
151
|
+
*/
|
|
152
|
+
export interface MCPServerConfig {
|
|
153
|
+
/** Command to run (for stdio transport) */
|
|
154
|
+
command?: string;
|
|
155
|
+
/** Arguments for the command */
|
|
156
|
+
args?: string[];
|
|
157
|
+
/** URL for HTTP/SSE/WebSocket transport */
|
|
158
|
+
url?: string;
|
|
159
|
+
/** Transport type */
|
|
160
|
+
transport?: MCPTransport;
|
|
161
|
+
/** Environment variables */
|
|
162
|
+
env?: Record<string, string> | MCPEnvVar[];
|
|
163
|
+
/** Headers for HTTP transport */
|
|
164
|
+
headers?: Record<string, string>;
|
|
165
|
+
/** Human-readable description */
|
|
166
|
+
description?: string;
|
|
167
|
+
/** Whether the server is always enabled (bypasses project-scoped approval) */
|
|
168
|
+
alwaysAllow?: boolean;
|
|
169
|
+
/** Timeout in milliseconds */
|
|
170
|
+
timeout?: number;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* MCP Servers configuration (from ~/.config/claude-code/config.json)
|
|
175
|
+
*/
|
|
176
|
+
export interface MCPServersConfig {
|
|
177
|
+
mcpServers: Record<string, MCPServerConfig>;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* MCP Server with metadata
|
|
182
|
+
*/
|
|
183
|
+
export interface MCPServerWithMeta extends MCPServerConfig {
|
|
184
|
+
name: string;
|
|
185
|
+
health?: 'connected' | 'disconnected' | 'error';
|
|
186
|
+
error?: string;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* MCP add options
|
|
191
|
+
*/
|
|
192
|
+
export interface MCPAddOptions {
|
|
193
|
+
name: string;
|
|
194
|
+
command?: string;
|
|
195
|
+
args?: string[];
|
|
196
|
+
url?: string;
|
|
197
|
+
transport?: MCPTransport;
|
|
198
|
+
env?: Record<string, string> | MCPEnvVar[];
|
|
199
|
+
headers?: Record<string, string>;
|
|
200
|
+
description?: string;
|
|
201
|
+
alwaysAllow?: boolean;
|
|
202
|
+
timeout?: number;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* MCP install source types
|
|
207
|
+
*/
|
|
208
|
+
export type MCPInstallSource = 'npm' | 'github' | 'local' | 'url';
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* MCP config target (which config file to modify)
|
|
212
|
+
*/
|
|
213
|
+
export type MCPTarget = 'cli' | 'app' | 'both';
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* MCP install options
|
|
217
|
+
*/
|
|
218
|
+
export interface MCPInstallOptions {
|
|
219
|
+
/** Source type (auto-detected if not specified) */
|
|
220
|
+
source?: MCPInstallSource;
|
|
221
|
+
/** Package name (npm) or repo URL (github) or local path */
|
|
222
|
+
package: string;
|
|
223
|
+
/** Name for the MCP server (defaults to package name) */
|
|
224
|
+
name?: string;
|
|
225
|
+
/** Version to install (for npm) */
|
|
226
|
+
version?: string;
|
|
227
|
+
/** Environment variables for the server */
|
|
228
|
+
env?: Record<string, string>;
|
|
229
|
+
/** Description for the server */
|
|
230
|
+
description?: string;
|
|
231
|
+
/** Arguments to pass to the server */
|
|
232
|
+
args?: string[];
|
|
233
|
+
/** Global packages directory (defaults to ~/.claude/mcp-servers) */
|
|
234
|
+
installDir?: string;
|
|
235
|
+
/** Force reinstallation */
|
|
236
|
+
force?: boolean;
|
|
237
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// Error Handling - Categorization, retry logic, and user-friendly messages
|
|
4
|
+
// ============================================================================
|
|
5
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
6
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
7
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
8
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
9
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
10
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
11
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
15
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
16
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
17
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
18
|
+
function step(op) {
|
|
19
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
20
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
21
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
22
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
23
|
+
switch (op[0]) {
|
|
24
|
+
case 0: case 1: t = op; break;
|
|
25
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
26
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
27
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
28
|
+
default:
|
|
29
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
30
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
31
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
32
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
33
|
+
if (t[2]) _.ops.pop();
|
|
34
|
+
_.trys.pop(); continue;
|
|
35
|
+
}
|
|
36
|
+
op = body.call(thisArg, _);
|
|
37
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
38
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.ErrorCode = exports.ErrorCategory = void 0;
|
|
43
|
+
exports.categorizeError = categorizeError;
|
|
44
|
+
exports.getUserAction = getUserAction;
|
|
45
|
+
exports.isRetryable = isRetryable;
|
|
46
|
+
exports.retryWithBackoff = retryWithBackoff;
|
|
47
|
+
exports.createMCPError = createMCPError;
|
|
48
|
+
exports.getErrorCode = getErrorCode;
|
|
49
|
+
var ErrorCategory;
|
|
50
|
+
(function (ErrorCategory) {
|
|
51
|
+
ErrorCategory["TRANSIENT"] = "TRANSIENT";
|
|
52
|
+
ErrorCategory["PERMISSION"] = "PERMISSION";
|
|
53
|
+
ErrorCategory["VALIDATION"] = "VALIDATION";
|
|
54
|
+
ErrorCategory["SYSTEM"] = "SYSTEM";
|
|
55
|
+
ErrorCategory["UNKNOWN"] = "UNKNOWN";
|
|
56
|
+
})(ErrorCategory || (exports.ErrorCategory = ErrorCategory = {}));
|
|
57
|
+
/**
|
|
58
|
+
* Categorize an error for appropriate handling
|
|
59
|
+
*/
|
|
60
|
+
function categorizeError(error) {
|
|
61
|
+
if (!(error instanceof Error)) {
|
|
62
|
+
return ErrorCategory.UNKNOWN;
|
|
63
|
+
}
|
|
64
|
+
var message = error.message.toLowerCase();
|
|
65
|
+
// Permission errors
|
|
66
|
+
if (message.includes('eacces') ||
|
|
67
|
+
message.includes('eperm') ||
|
|
68
|
+
message.includes('permission denied')) {
|
|
69
|
+
return ErrorCategory.PERMISSION;
|
|
70
|
+
}
|
|
71
|
+
// File not found (validation)
|
|
72
|
+
if (message.includes('enoent') || message.includes('no such file')) {
|
|
73
|
+
return ErrorCategory.VALIDATION;
|
|
74
|
+
}
|
|
75
|
+
// Transient errors (retryable)
|
|
76
|
+
if (message.includes('eagain') ||
|
|
77
|
+
message.includes('etimedout') ||
|
|
78
|
+
message.includes('econnreset') ||
|
|
79
|
+
message.includes('econnrefused') ||
|
|
80
|
+
message.includes('timeout') ||
|
|
81
|
+
message.includes('temporary failure')) {
|
|
82
|
+
return ErrorCategory.TRANSIENT;
|
|
83
|
+
}
|
|
84
|
+
// System errors
|
|
85
|
+
if (message.includes('enospc') ||
|
|
86
|
+
message.includes('disk full') ||
|
|
87
|
+
message.includes('eisdir') ||
|
|
88
|
+
message.includes('enotdir')) {
|
|
89
|
+
return ErrorCategory.SYSTEM;
|
|
90
|
+
}
|
|
91
|
+
return ErrorCategory.UNKNOWN;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get user-friendly action message for an error
|
|
95
|
+
*/
|
|
96
|
+
function getUserAction(category) {
|
|
97
|
+
switch (category) {
|
|
98
|
+
case ErrorCategory.PERMISSION:
|
|
99
|
+
return 'Check file permissions and ensure Claude Code has access to the config directory';
|
|
100
|
+
case ErrorCategory.VALIDATION:
|
|
101
|
+
return 'Verify the file path and try again';
|
|
102
|
+
case ErrorCategory.SYSTEM:
|
|
103
|
+
return 'Check system resources and Claude Code configuration';
|
|
104
|
+
case ErrorCategory.TRANSIENT:
|
|
105
|
+
return 'The operation failed temporarily. Please try again';
|
|
106
|
+
default:
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Check if an error is retryable
|
|
112
|
+
*/
|
|
113
|
+
function isRetryable(error) {
|
|
114
|
+
return categorizeError(error) === ErrorCategory.TRANSIENT;
|
|
115
|
+
}
|
|
116
|
+
var DEFAULT_RETRY_OPTIONS = {
|
|
117
|
+
maxAttempts: 3,
|
|
118
|
+
baseDelay: 1000,
|
|
119
|
+
maxDelay: 10000,
|
|
120
|
+
jitter: true,
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Retry an operation with exponential backoff
|
|
124
|
+
*/
|
|
125
|
+
function retryWithBackoff(operation_1) {
|
|
126
|
+
return __awaiter(this, arguments, void 0, function (operation, options) {
|
|
127
|
+
var lastError, _loop_1, attempt, state_1;
|
|
128
|
+
if (options === void 0) { options = DEFAULT_RETRY_OPTIONS; }
|
|
129
|
+
return __generator(this, function (_a) {
|
|
130
|
+
switch (_a.label) {
|
|
131
|
+
case 0:
|
|
132
|
+
_loop_1 = function (attempt) {
|
|
133
|
+
var _b, error_1, delay, finalDelay_1;
|
|
134
|
+
return __generator(this, function (_c) {
|
|
135
|
+
switch (_c.label) {
|
|
136
|
+
case 0:
|
|
137
|
+
_c.trys.push([0, 2, , 4]);
|
|
138
|
+
_b = {};
|
|
139
|
+
return [4 /*yield*/, operation()];
|
|
140
|
+
case 1: return [2 /*return*/, (_b.value = _c.sent(), _b)];
|
|
141
|
+
case 2:
|
|
142
|
+
error_1 = _c.sent();
|
|
143
|
+
lastError = error_1;
|
|
144
|
+
// Don't retry if not retryable
|
|
145
|
+
if (!isRetryable(error_1)) {
|
|
146
|
+
throw error_1;
|
|
147
|
+
}
|
|
148
|
+
delay = Math.min(options.baseDelay * Math.pow(2, attempt), options.maxDelay);
|
|
149
|
+
finalDelay_1 = options.jitter
|
|
150
|
+
? delay + Math.random() * 1000
|
|
151
|
+
: delay;
|
|
152
|
+
// Wait before retry
|
|
153
|
+
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, finalDelay_1); })];
|
|
154
|
+
case 3:
|
|
155
|
+
// Wait before retry
|
|
156
|
+
_c.sent();
|
|
157
|
+
return [3 /*break*/, 4];
|
|
158
|
+
case 4: return [2 /*return*/];
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
attempt = 0;
|
|
163
|
+
_a.label = 1;
|
|
164
|
+
case 1:
|
|
165
|
+
if (!(attempt < options.maxAttempts)) return [3 /*break*/, 4];
|
|
166
|
+
return [5 /*yield**/, _loop_1(attempt)];
|
|
167
|
+
case 2:
|
|
168
|
+
state_1 = _a.sent();
|
|
169
|
+
if (typeof state_1 === "object")
|
|
170
|
+
return [2 /*return*/, state_1.value];
|
|
171
|
+
_a.label = 3;
|
|
172
|
+
case 3:
|
|
173
|
+
attempt++;
|
|
174
|
+
return [3 /*break*/, 1];
|
|
175
|
+
case 4: throw lastError;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
function createMCPError(error) {
|
|
181
|
+
var category = categorizeError(error);
|
|
182
|
+
var message = error instanceof Error ? error.message : 'Unknown error';
|
|
183
|
+
return {
|
|
184
|
+
success: false,
|
|
185
|
+
error: {
|
|
186
|
+
category: category,
|
|
187
|
+
message: message,
|
|
188
|
+
retryable: category === ErrorCategory.TRANSIENT,
|
|
189
|
+
userAction: getUserAction(category),
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Error codes for different scenarios
|
|
195
|
+
*/
|
|
196
|
+
var ErrorCode;
|
|
197
|
+
(function (ErrorCode) {
|
|
198
|
+
ErrorCode["FILE_NOT_FOUND"] = "FILE_NOT_FOUND";
|
|
199
|
+
ErrorCode["PERMISSION_DENIED"] = "PERMISSION_DENIED";
|
|
200
|
+
ErrorCode["INVALID_JSON"] = "INVALID_JSON";
|
|
201
|
+
ErrorCode["VALIDATION_FAILED"] = "VALIDATION_FAILED";
|
|
202
|
+
ErrorCode["PATH_TRAVERSAL"] = "PATH_TRAVERSAL";
|
|
203
|
+
ErrorCode["DISK_FULL"] = "DISK_FULL";
|
|
204
|
+
ErrorCode["LOCKED"] = "LOCKED";
|
|
205
|
+
ErrorCode["UNKNOWN"] = "UNKNOWN";
|
|
206
|
+
})(ErrorCode || (exports.ErrorCode = ErrorCode = {}));
|
|
207
|
+
/**
|
|
208
|
+
* Get error code from error
|
|
209
|
+
*/
|
|
210
|
+
function getErrorCode(error) {
|
|
211
|
+
var category = categorizeError(error);
|
|
212
|
+
switch (category) {
|
|
213
|
+
case ErrorCategory.VALIDATION:
|
|
214
|
+
if (error instanceof Error && error.message.includes('JSON')) {
|
|
215
|
+
return ErrorCode.INVALID_JSON;
|
|
216
|
+
}
|
|
217
|
+
return ErrorCode.FILE_NOT_FOUND;
|
|
218
|
+
case ErrorCategory.PERMISSION:
|
|
219
|
+
return ErrorCode.PERMISSION_DENIED;
|
|
220
|
+
case ErrorCategory.SYSTEM:
|
|
221
|
+
if (error instanceof Error && error.message.includes('enospc')) {
|
|
222
|
+
return ErrorCode.DISK_FULL;
|
|
223
|
+
}
|
|
224
|
+
if (error instanceof Error && error.message.includes('etxtbsy')) {
|
|
225
|
+
return ErrorCode.LOCKED;
|
|
226
|
+
}
|
|
227
|
+
return ErrorCode.UNKNOWN;
|
|
228
|
+
default:
|
|
229
|
+
return ErrorCode.UNKNOWN;
|
|
230
|
+
}
|
|
231
|
+
}
|