@everstateai/mcp 1.3.1 → 1.3.3
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/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -5
- package/dist/index.js.map +1 -1
- package/dist/setup/auto-update.d.ts +20 -0
- package/dist/setup/auto-update.d.ts.map +1 -0
- package/dist/setup/auto-update.js +295 -0
- package/dist/setup/auto-update.js.map +1 -0
- package/dist/setup/commands/doctor.d.ts +15 -0
- package/dist/setup/commands/doctor.d.ts.map +1 -0
- package/dist/setup/commands/doctor.js +264 -0
- package/dist/setup/commands/doctor.js.map +1 -0
- package/dist/setup/commands/repair.d.ts +14 -0
- package/dist/setup/commands/repair.d.ts.map +1 -0
- package/dist/setup/commands/repair.js +252 -0
- package/dist/setup/commands/repair.js.map +1 -0
- package/dist/setup/hooks/templates.d.ts +30 -0
- package/dist/setup/hooks/templates.d.ts.map +1 -0
- package/dist/setup/hooks/templates.js +237 -0
- package/dist/setup/hooks/templates.js.map +1 -0
- package/dist/setup/types.d.ts +120 -0
- package/dist/setup/types.d.ts.map +1 -0
- package/dist/setup/types.js +58 -0
- package/dist/setup/types.js.map +1 -0
- package/dist/setup/validators/api-key.d.ts +8 -0
- package/dist/setup/validators/api-key.d.ts.map +1 -0
- package/dist/setup/validators/api-key.js +233 -0
- package/dist/setup/validators/api-key.js.map +1 -0
- package/dist/setup/validators/connectivity.d.ts +8 -0
- package/dist/setup/validators/connectivity.d.ts.map +1 -0
- package/dist/setup/validators/connectivity.js +150 -0
- package/dist/setup/validators/connectivity.js.map +1 -0
- package/dist/setup/validators/hooks.d.ts +8 -0
- package/dist/setup/validators/hooks.d.ts.map +1 -0
- package/dist/setup/validators/hooks.js +431 -0
- package/dist/setup/validators/hooks.js.map +1 -0
- package/dist/setup/validators/index.d.ts +18 -0
- package/dist/setup/validators/index.d.ts.map +1 -0
- package/dist/setup/validators/index.js +123 -0
- package/dist/setup/validators/index.js.map +1 -0
- package/dist/setup/validators/mcp-config.d.ts +8 -0
- package/dist/setup/validators/mcp-config.d.ts.map +1 -0
- package/dist/setup/validators/mcp-config.js +333 -0
- package/dist/setup/validators/mcp-config.js.map +1 -0
- package/dist/setup/validators/project.d.ts +8 -0
- package/dist/setup/validators/project.d.ts.map +1 -0
- package/dist/setup/validators/project.js +202 -0
- package/dist/setup/validators/project.js.map +1 -0
- package/dist/setup/version.d.ts +58 -0
- package/dist/setup/version.d.ts.map +1 -0
- package/dist/setup/version.js +262 -0
- package/dist/setup/version.js.map +1 -0
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +264 -28
- package/dist/setup.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +32 -5
- package/src/setup/auto-update.ts +328 -0
- package/src/setup/commands/doctor.ts +266 -0
- package/src/setup/commands/repair.ts +260 -0
- package/src/setup/hooks/templates.ts +239 -0
- package/src/setup/types.ts +199 -0
- package/src/setup/validators/api-key.ts +218 -0
- package/src/setup/validators/connectivity.ts +176 -0
- package/src/setup/validators/hooks.ts +447 -0
- package/src/setup/validators/index.ts +137 -0
- package/src/setup/validators/mcp-config.ts +329 -0
- package/src/setup/validators/project.ts +179 -0
- package/src/setup/version.ts +267 -0
- package/src/setup.ts +297 -28
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Configuration Validator
|
|
3
|
+
*
|
|
4
|
+
* Validates that Claude Code and Claude Desktop have correct MCP configuration.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import {
|
|
9
|
+
Validator,
|
|
10
|
+
ValidationResult,
|
|
11
|
+
ValidationContext,
|
|
12
|
+
getClaudeConfigPath,
|
|
13
|
+
getClaudeDesktopConfigPath,
|
|
14
|
+
getEverstateDir,
|
|
15
|
+
} from '../types.js';
|
|
16
|
+
|
|
17
|
+
export const mcpConfigValidator: Validator = {
|
|
18
|
+
name: 'MCP Configuration',
|
|
19
|
+
|
|
20
|
+
async validate(context: ValidationContext): Promise<ValidationResult[]> {
|
|
21
|
+
const results: ValidationResult[] = [];
|
|
22
|
+
|
|
23
|
+
// Check Claude Code config
|
|
24
|
+
const claudeCodeResults = await validateClaudeCodeConfig();
|
|
25
|
+
results.push(...claudeCodeResults);
|
|
26
|
+
|
|
27
|
+
// Check Claude Desktop config (optional)
|
|
28
|
+
const desktopPath = getClaudeDesktopConfigPath();
|
|
29
|
+
if (desktopPath && fs.existsSync(desktopPath)) {
|
|
30
|
+
const desktopResults = await validateClaudeDesktopConfig(desktopPath);
|
|
31
|
+
results.push(...desktopResults);
|
|
32
|
+
} else if (desktopPath) {
|
|
33
|
+
results.push({
|
|
34
|
+
component: 'MCP Config',
|
|
35
|
+
check: 'Claude Desktop',
|
|
36
|
+
status: 'warn',
|
|
37
|
+
message: 'Claude Desktop not installed or not configured',
|
|
38
|
+
details: { path: desktopPath },
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return results;
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
async repair(result: ValidationResult, context: ValidationContext): Promise<boolean> {
|
|
46
|
+
if (result.check === 'Claude Code config' && result.repairAction?.type === 'install') {
|
|
47
|
+
return await repairClaudeCodeConfig();
|
|
48
|
+
}
|
|
49
|
+
if (result.check === 'Everstate MCP entry' && result.repairAction?.type === 'configure') {
|
|
50
|
+
return await repairEverstateEntry();
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
async function validateClaudeCodeConfig(): Promise<ValidationResult[]> {
|
|
57
|
+
const results: ValidationResult[] = [];
|
|
58
|
+
const configPath = getClaudeConfigPath();
|
|
59
|
+
|
|
60
|
+
// Check 1: Config file exists
|
|
61
|
+
if (!fs.existsSync(configPath)) {
|
|
62
|
+
results.push({
|
|
63
|
+
component: 'MCP Config',
|
|
64
|
+
check: 'Claude Code config',
|
|
65
|
+
status: 'fail',
|
|
66
|
+
message: `Config not found at ${configPath}`,
|
|
67
|
+
repairAction: {
|
|
68
|
+
type: 'install',
|
|
69
|
+
description: 'Create Claude Code configuration',
|
|
70
|
+
automatic: true,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
return results;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check 2: Valid JSON
|
|
77
|
+
let config: Record<string, unknown>;
|
|
78
|
+
try {
|
|
79
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
80
|
+
config = JSON.parse(content);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
results.push({
|
|
83
|
+
component: 'MCP Config',
|
|
84
|
+
check: 'Claude Code config',
|
|
85
|
+
status: 'fail',
|
|
86
|
+
message: `Invalid JSON: ${err instanceof Error ? err.message : 'Parse error'}`,
|
|
87
|
+
repairAction: {
|
|
88
|
+
type: 'configure',
|
|
89
|
+
description: 'Fix JSON syntax or restore backup',
|
|
90
|
+
automatic: false,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
return results;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
results.push({
|
|
97
|
+
component: 'MCP Config',
|
|
98
|
+
check: 'Claude Code config',
|
|
99
|
+
status: 'pass',
|
|
100
|
+
message: `Valid at ${configPath}`,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Check 3: mcpServers exists
|
|
104
|
+
const mcpServers = config.mcpServers as Record<string, unknown> | undefined;
|
|
105
|
+
if (!mcpServers) {
|
|
106
|
+
results.push({
|
|
107
|
+
component: 'MCP Config',
|
|
108
|
+
check: 'mcpServers section',
|
|
109
|
+
status: 'fail',
|
|
110
|
+
message: 'No mcpServers section in config',
|
|
111
|
+
repairAction: {
|
|
112
|
+
type: 'configure',
|
|
113
|
+
description: 'Add mcpServers configuration',
|
|
114
|
+
automatic: true,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
return results;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Check 4: Everstate entry exists
|
|
121
|
+
const everstateConfig = mcpServers.everstate as Record<string, unknown> | undefined;
|
|
122
|
+
if (!everstateConfig) {
|
|
123
|
+
results.push({
|
|
124
|
+
component: 'MCP Config',
|
|
125
|
+
check: 'Everstate MCP entry',
|
|
126
|
+
status: 'fail',
|
|
127
|
+
message: 'Everstate not configured in mcpServers',
|
|
128
|
+
repairAction: {
|
|
129
|
+
type: 'configure',
|
|
130
|
+
description: 'Add Everstate MCP server configuration',
|
|
131
|
+
automatic: true,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
return results;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
results.push({
|
|
138
|
+
component: 'MCP Config',
|
|
139
|
+
check: 'Everstate MCP entry',
|
|
140
|
+
status: 'pass',
|
|
141
|
+
message: 'Everstate MCP server configured',
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Check 5: Command is valid
|
|
145
|
+
const command = everstateConfig.command as string | undefined;
|
|
146
|
+
const args = everstateConfig.args as string[] | undefined;
|
|
147
|
+
|
|
148
|
+
if (command === 'npx' && args?.includes('@everstateai/mcp')) {
|
|
149
|
+
results.push({
|
|
150
|
+
component: 'MCP Config',
|
|
151
|
+
check: 'MCP command',
|
|
152
|
+
status: 'pass',
|
|
153
|
+
message: 'Using npx @everstateai/mcp (recommended)',
|
|
154
|
+
});
|
|
155
|
+
} else if (command === 'node') {
|
|
156
|
+
results.push({
|
|
157
|
+
component: 'MCP Config',
|
|
158
|
+
check: 'MCP command',
|
|
159
|
+
status: 'warn',
|
|
160
|
+
message: 'Using local node path - consider using npx for auto-updates',
|
|
161
|
+
details: { command, args },
|
|
162
|
+
});
|
|
163
|
+
} else {
|
|
164
|
+
results.push({
|
|
165
|
+
component: 'MCP Config',
|
|
166
|
+
check: 'MCP command',
|
|
167
|
+
status: 'warn',
|
|
168
|
+
message: `Non-standard command: ${command}`,
|
|
169
|
+
details: { command, args },
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Check 6: Environment variables
|
|
174
|
+
const env = everstateConfig.env as Record<string, string> | undefined;
|
|
175
|
+
if (!env?.EVERSTATE_API_KEY) {
|
|
176
|
+
// Check if API key is in file instead
|
|
177
|
+
const apiKeyPath = `${getEverstateDir()}/api-key`;
|
|
178
|
+
if (!fs.existsSync(apiKeyPath)) {
|
|
179
|
+
results.push({
|
|
180
|
+
component: 'MCP Config',
|
|
181
|
+
check: 'API key configuration',
|
|
182
|
+
status: 'fail',
|
|
183
|
+
message: 'No API key in MCP config or key file',
|
|
184
|
+
repairAction: {
|
|
185
|
+
type: 'configure',
|
|
186
|
+
description: 'Configure API key',
|
|
187
|
+
automatic: false,
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
} else {
|
|
191
|
+
results.push({
|
|
192
|
+
component: 'MCP Config',
|
|
193
|
+
check: 'API key configuration',
|
|
194
|
+
status: 'pass',
|
|
195
|
+
message: 'Using API key from file (hooks will read it)',
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
results.push({
|
|
200
|
+
component: 'MCP Config',
|
|
201
|
+
check: 'API key configuration',
|
|
202
|
+
status: 'pass',
|
|
203
|
+
message: 'API key configured in MCP env',
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return results;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function validateClaudeDesktopConfig(configPath: string): Promise<ValidationResult[]> {
|
|
211
|
+
const results: ValidationResult[] = [];
|
|
212
|
+
|
|
213
|
+
// Similar validation for Claude Desktop
|
|
214
|
+
let config: Record<string, unknown>;
|
|
215
|
+
try {
|
|
216
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
217
|
+
config = JSON.parse(content);
|
|
218
|
+
} catch (err) {
|
|
219
|
+
results.push({
|
|
220
|
+
component: 'MCP Config',
|
|
221
|
+
check: 'Claude Desktop config',
|
|
222
|
+
status: 'warn',
|
|
223
|
+
message: `Could not parse: ${err instanceof Error ? err.message : 'Error'}`,
|
|
224
|
+
});
|
|
225
|
+
return results;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const mcpServers = config.mcpServers as Record<string, unknown> | undefined;
|
|
229
|
+
if (!mcpServers?.everstate) {
|
|
230
|
+
results.push({
|
|
231
|
+
component: 'MCP Config',
|
|
232
|
+
check: 'Claude Desktop Everstate',
|
|
233
|
+
status: 'warn',
|
|
234
|
+
message: 'Everstate not configured for Claude Desktop',
|
|
235
|
+
repairAction: {
|
|
236
|
+
type: 'configure',
|
|
237
|
+
description: 'Add Everstate to Claude Desktop',
|
|
238
|
+
automatic: true,
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
} else {
|
|
242
|
+
results.push({
|
|
243
|
+
component: 'MCP Config',
|
|
244
|
+
check: 'Claude Desktop Everstate',
|
|
245
|
+
status: 'pass',
|
|
246
|
+
message: 'Everstate configured for Claude Desktop',
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return results;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async function repairClaudeCodeConfig(): Promise<boolean> {
|
|
254
|
+
const configPath = getClaudeConfigPath();
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
// Create minimal config
|
|
258
|
+
const config = {
|
|
259
|
+
mcpServers: {
|
|
260
|
+
everstate: {
|
|
261
|
+
command: 'npx',
|
|
262
|
+
args: ['-y', '@everstateai/mcp'],
|
|
263
|
+
env: {},
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
// Check for API key
|
|
269
|
+
const apiKeyPath = `${getEverstateDir()}/api-key`;
|
|
270
|
+
if (fs.existsSync(apiKeyPath)) {
|
|
271
|
+
const apiKey = fs.readFileSync(apiKeyPath, 'utf8').trim();
|
|
272
|
+
if (apiKey) {
|
|
273
|
+
(config.mcpServers.everstate.env as Record<string, string>).EVERSTATE_API_KEY = apiKey;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
278
|
+
return true;
|
|
279
|
+
} catch {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async function repairEverstateEntry(): Promise<boolean> {
|
|
285
|
+
const configPath = getClaudeConfigPath();
|
|
286
|
+
|
|
287
|
+
try {
|
|
288
|
+
let config: Record<string, unknown> = {};
|
|
289
|
+
|
|
290
|
+
if (fs.existsSync(configPath)) {
|
|
291
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
292
|
+
config = JSON.parse(content);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (!config.mcpServers) {
|
|
296
|
+
config.mcpServers = {};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const mcpServers = config.mcpServers as Record<string, unknown>;
|
|
300
|
+
|
|
301
|
+
// Add Everstate entry
|
|
302
|
+
const everstateConfig: Record<string, unknown> = {
|
|
303
|
+
command: 'npx',
|
|
304
|
+
args: ['-y', '@everstateai/mcp'],
|
|
305
|
+
env: {},
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
// Check for API key
|
|
309
|
+
const apiKeyPath = `${getEverstateDir()}/api-key`;
|
|
310
|
+
if (fs.existsSync(apiKeyPath)) {
|
|
311
|
+
const apiKey = fs.readFileSync(apiKeyPath, 'utf8').trim();
|
|
312
|
+
if (apiKey) {
|
|
313
|
+
(everstateConfig.env as Record<string, string>).EVERSTATE_API_KEY = apiKey;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
mcpServers.everstate = everstateConfig;
|
|
318
|
+
|
|
319
|
+
// Backup existing
|
|
320
|
+
if (fs.existsSync(configPath)) {
|
|
321
|
+
fs.copyFileSync(configPath, `${configPath}.backup`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
325
|
+
return true;
|
|
326
|
+
} catch {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Validator
|
|
3
|
+
*
|
|
4
|
+
* Validates project-level configuration (.everstate.json).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import {
|
|
10
|
+
Validator,
|
|
11
|
+
ValidationResult,
|
|
12
|
+
ValidationContext,
|
|
13
|
+
EVERSTATE_API_URL,
|
|
14
|
+
} from '../types.js';
|
|
15
|
+
|
|
16
|
+
export const projectValidator: Validator = {
|
|
17
|
+
name: 'Project',
|
|
18
|
+
|
|
19
|
+
async validate(context: ValidationContext): Promise<ValidationResult[]> {
|
|
20
|
+
const results: ValidationResult[] = [];
|
|
21
|
+
|
|
22
|
+
if (!context.projectDir) {
|
|
23
|
+
results.push({
|
|
24
|
+
component: 'Project',
|
|
25
|
+
check: 'Project directory',
|
|
26
|
+
status: 'warn',
|
|
27
|
+
message: 'No project directory specified (running globally)',
|
|
28
|
+
});
|
|
29
|
+
return results;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const configPath = path.join(context.projectDir, '.everstate.json');
|
|
33
|
+
|
|
34
|
+
// Check 1: Config file exists
|
|
35
|
+
if (!fs.existsSync(configPath)) {
|
|
36
|
+
results.push({
|
|
37
|
+
component: 'Project',
|
|
38
|
+
check: '.everstate.json exists',
|
|
39
|
+
status: 'fail',
|
|
40
|
+
message: `Not found at ${configPath}`,
|
|
41
|
+
repairAction: {
|
|
42
|
+
type: 'install',
|
|
43
|
+
description: 'Initialize project with everstate',
|
|
44
|
+
automatic: false, // Requires user input for project name
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
return results;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
results.push({
|
|
51
|
+
component: 'Project',
|
|
52
|
+
check: '.everstate.json exists',
|
|
53
|
+
status: 'pass',
|
|
54
|
+
message: `Found at ${configPath}`,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Check 2: Valid JSON
|
|
58
|
+
let config: Record<string, unknown>;
|
|
59
|
+
try {
|
|
60
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
61
|
+
config = JSON.parse(content);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
results.push({
|
|
64
|
+
component: 'Project',
|
|
65
|
+
check: '.everstate.json valid',
|
|
66
|
+
status: 'fail',
|
|
67
|
+
message: 'Invalid JSON syntax',
|
|
68
|
+
details: { error: String(error) },
|
|
69
|
+
repairAction: {
|
|
70
|
+
type: 'configure',
|
|
71
|
+
description: 'Fix JSON syntax in .everstate.json',
|
|
72
|
+
automatic: false,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
return results;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
results.push({
|
|
79
|
+
component: 'Project',
|
|
80
|
+
check: '.everstate.json valid',
|
|
81
|
+
status: 'pass',
|
|
82
|
+
message: 'Valid JSON',
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Check 3: Has projectId
|
|
86
|
+
const projectId = config.projectId as string | undefined;
|
|
87
|
+
if (!projectId) {
|
|
88
|
+
results.push({
|
|
89
|
+
component: 'Project',
|
|
90
|
+
check: 'Project ID',
|
|
91
|
+
status: 'fail',
|
|
92
|
+
message: 'Missing projectId field',
|
|
93
|
+
repairAction: {
|
|
94
|
+
type: 'configure',
|
|
95
|
+
description: 'Add projectId to .everstate.json',
|
|
96
|
+
automatic: false,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
return results;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Check 4: Valid projectId format
|
|
103
|
+
if (!projectId.startsWith('proj_')) {
|
|
104
|
+
results.push({
|
|
105
|
+
component: 'Project',
|
|
106
|
+
check: 'Project ID format',
|
|
107
|
+
status: 'warn',
|
|
108
|
+
message: `Unusual format: ${projectId} (expected proj_...)`,
|
|
109
|
+
details: { projectId },
|
|
110
|
+
});
|
|
111
|
+
} else {
|
|
112
|
+
results.push({
|
|
113
|
+
component: 'Project',
|
|
114
|
+
check: 'Project ID format',
|
|
115
|
+
status: 'pass',
|
|
116
|
+
message: `Valid format: ${projectId.substring(0, 20)}...`,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Check 5: Verify project exists on server (if not skipping network)
|
|
121
|
+
if (!context.skipNetwork && context.apiKey) {
|
|
122
|
+
try {
|
|
123
|
+
const response = await fetch(`${EVERSTATE_API_URL}/api/project/${projectId}`, {
|
|
124
|
+
headers: {
|
|
125
|
+
Authorization: `Bearer ${context.apiKey}`,
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
if (response.ok) {
|
|
130
|
+
const data = (await response.json()) as { name?: string };
|
|
131
|
+
results.push({
|
|
132
|
+
component: 'Project',
|
|
133
|
+
check: 'Project exists on server',
|
|
134
|
+
status: 'pass',
|
|
135
|
+
message: `Project "${data.name || projectId}" found`,
|
|
136
|
+
});
|
|
137
|
+
} else if (response.status === 404) {
|
|
138
|
+
results.push({
|
|
139
|
+
component: 'Project',
|
|
140
|
+
check: 'Project exists on server',
|
|
141
|
+
status: 'warn',
|
|
142
|
+
message: 'Project not found on server (may need to sync)',
|
|
143
|
+
details: { projectId },
|
|
144
|
+
});
|
|
145
|
+
} else if (response.status === 401) {
|
|
146
|
+
results.push({
|
|
147
|
+
component: 'Project',
|
|
148
|
+
check: 'Project exists on server',
|
|
149
|
+
status: 'warn',
|
|
150
|
+
message: 'Cannot verify - API key may not have access',
|
|
151
|
+
});
|
|
152
|
+
} else {
|
|
153
|
+
results.push({
|
|
154
|
+
component: 'Project',
|
|
155
|
+
check: 'Project exists on server',
|
|
156
|
+
status: 'warn',
|
|
157
|
+
message: `Unexpected response: ${response.status}`,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
} catch (error) {
|
|
161
|
+
results.push({
|
|
162
|
+
component: 'Project',
|
|
163
|
+
check: 'Project exists on server',
|
|
164
|
+
status: 'warn',
|
|
165
|
+
message: 'Could not verify (network error)',
|
|
166
|
+
details: { error: String(error) },
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return results;
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
async repair(result: ValidationResult, context: ValidationContext): Promise<boolean> {
|
|
175
|
+
// Project repairs typically require user input
|
|
176
|
+
// The setup command handles project initialization
|
|
177
|
+
return false;
|
|
178
|
+
},
|
|
179
|
+
};
|