@vibecodetown/mcp-server 2.1.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 +269 -0
- package/build/auth/gate.js +225 -0
- package/build/auth/index.js +55 -0
- package/build/auth/public_key.js +27 -0
- package/build/auth/token_cache.js +122 -0
- package/build/auth/token_verifier.js +103 -0
- package/build/bootstrap/doctor.js +115 -0
- package/build/bootstrap/installer.js +673 -0
- package/build/bootstrap/lock.js +37 -0
- package/build/bootstrap/platform.js +26 -0
- package/build/bootstrap/registry.js +37 -0
- package/build/cache/index.js +147 -0
- package/build/cli.js +101 -0
- package/build/contracts.js +22 -0
- package/build/control_plane/gate.js +161 -0
- package/build/control_plane/index.js +6 -0
- package/build/dx/activity.js +139 -0
- package/build/engine.js +106 -0
- package/build/errors.js +171 -0
- package/build/generated/activate_input.js +2 -0
- package/build/generated/activate_output.js +57 -0
- package/build/generated/advisory_review_input.js +2 -0
- package/build/generated/advisory_review_output.js +35 -0
- package/build/generated/auth_token_file.js +2 -0
- package/build/generated/briefing_input.js +2 -0
- package/build/generated/briefing_output.js +2 -0
- package/build/generated/clinic_bridge_file.js +13 -0
- package/build/generated/contracts_bundle_info.js +5 -0
- package/build/generated/create_work_order_input.js +2 -0
- package/build/generated/create_work_order_output.js +2 -0
- package/build/generated/current_work_order_file.js +2 -0
- package/build/generated/doctor_input.js +2 -0
- package/build/generated/doctor_output.js +24 -0
- package/build/generated/execution_result.js +2 -0
- package/build/generated/execution_task.js +2 -0
- package/build/generated/export_output_input.js +2 -0
- package/build/generated/export_output_output.js +2 -0
- package/build/generated/finalize_work_input.js +2 -0
- package/build/generated/finalize_work_output.js +2 -0
- package/build/generated/gate_input.js +2 -0
- package/build/generated/gate_output.js +2 -0
- package/build/generated/gate_result_v1.js +2 -0
- package/build/generated/get_decision_input.js +2 -0
- package/build/generated/get_decision_output.js +13 -0
- package/build/generated/handoff_to_clinic.js +2 -0
- package/build/generated/index.js +75 -0
- package/build/generated/inspect_code_input.js +2 -0
- package/build/generated/inspect_code_output.js +13 -0
- package/build/generated/memory_retrieve_output.js +2 -0
- package/build/generated/memory_state_file.js +2 -0
- package/build/generated/memory_status_input.js +2 -0
- package/build/generated/memory_status_output.js +13 -0
- package/build/generated/memory_sync_input.js +2 -0
- package/build/generated/memory_sync_output.js +13 -0
- package/build/generated/plugin_result.js +2 -0
- package/build/generated/react_perf_check_patterns_input.js +2 -0
- package/build/generated/react_perf_check_patterns_output.js +2 -0
- package/build/generated/react_perf_generate_report_input.js +2 -0
- package/build/generated/react_perf_generate_report_output.js +2 -0
- package/build/generated/repair_plan_input.js +2 -0
- package/build/generated/repair_plan_output.js +2 -0
- package/build/generated/run_app_input.js +2 -0
- package/build/generated/run_app_output.js +2 -0
- package/build/generated/run_state_file.js +13 -0
- package/build/generated/scaffold_input.js +2 -0
- package/build/generated/scaffold_output.js +2 -0
- package/build/generated/search_oss_input.js +2 -0
- package/build/generated/search_oss_output.js +2 -0
- package/build/generated/selection_validation_result.js +2 -0
- package/build/generated/signal_agent_input.js +2 -0
- package/build/generated/spec_high_ask_queue_items_file.js +2 -0
- package/build/generated/spec_high_clinic_bridge_output.js +2 -0
- package/build/generated/spec_high_decision_draft_output.js +2 -0
- package/build/generated/spec_high_validate_output.js +2 -0
- package/build/generated/status_input.js +2 -0
- package/build/generated/status_output.js +2 -0
- package/build/generated/submit_decision_input.js +2 -0
- package/build/generated/submit_decision_output.js +2 -0
- package/build/generated/tool_error_output.js +2 -0
- package/build/generated/undo_last_task_input.js +2 -0
- package/build/generated/undo_last_task_output.js +2 -0
- package/build/generated/update_input.js +2 -0
- package/build/generated/update_output.js +2 -0
- package/build/generated/vibe_pm_inspection_result.js +2 -0
- package/build/generated/vibe_pm_report_markdown.js +2 -0
- package/build/generated/vibe_pm_verdict.js +2 -0
- package/build/generated/vibe_repo_config.js +2 -0
- package/build/generated/vibecoding_helper_answer_output.js +2 -0
- package/build/generated/vibecoding_helper_one_loop_selection_output.js +2 -0
- package/build/generated/vibecoding_helper_show_ask_queue_output.js +2 -0
- package/build/generated/work_order_v1.js +2 -0
- package/build/generated/zoekt_evidence_input.js +2 -0
- package/build/generated/zoekt_evidence_output.js +2 -0
- package/build/index.js +111 -0
- package/build/legacy_alias.js +65 -0
- package/build/local-mode/bash.js +61 -0
- package/build/local-mode/config.js +171 -0
- package/build/local-mode/git.js +33 -0
- package/build/local-mode/init.js +110 -0
- package/build/local-mode/paths.js +24 -0
- package/build/local-mode/templates.js +856 -0
- package/build/local-mode/work-order.js +41 -0
- package/build/resources/index.js +246 -0
- package/build/security/input-validator.js +119 -0
- package/build/security/path-policy.js +289 -0
- package/build/security/sandbox.js +228 -0
- package/build/tools/react_perf/check_patterns.js +172 -0
- package/build/tools/react_perf/generate_report.js +337 -0
- package/build/tools/react_perf/index.js +119 -0
- package/build/tools/react_perf/rules/advanced.js +325 -0
- package/build/tools/react_perf/rules/async.js +104 -0
- package/build/tools/react_perf/rules/bundle.js +101 -0
- package/build/tools/react_perf/rules/client.js +186 -0
- package/build/tools/react_perf/rules/index.js +74 -0
- package/build/tools/react_perf/rules/js.js +148 -0
- package/build/tools/react_perf/rules/rendering.js +166 -0
- package/build/tools/react_perf/rules/rerender.js +161 -0
- package/build/tools/react_perf/rules/server.js +141 -0
- package/build/tools/react_perf/types.js +127 -0
- package/build/tools/vibe_pm/activate.js +102 -0
- package/build/tools/vibe_pm/advisory_review.js +77 -0
- package/build/tools/vibe_pm/briefing.js +178 -0
- package/build/tools/vibe_pm/context.js +439 -0
- package/build/tools/vibe_pm/create_work_order.js +271 -0
- package/build/tools/vibe_pm/doc_status_gate.js +370 -0
- package/build/tools/vibe_pm/doctor.js +262 -0
- package/build/tools/vibe_pm/entity_gate/preflight.js +78 -0
- package/build/tools/vibe_pm/export_output.js +135 -0
- package/build/tools/vibe_pm/finalize_work.js +393 -0
- package/build/tools/vibe_pm/gate.js +33 -0
- package/build/tools/vibe_pm/get_decision.js +281 -0
- package/build/tools/vibe_pm/index.js +593 -0
- package/build/tools/vibe_pm/inspect_code.js +828 -0
- package/build/tools/vibe_pm/intent/generator.js +294 -0
- package/build/tools/vibe_pm/intent/index.js +5 -0
- package/build/tools/vibe_pm/intent/prompt_density.js +227 -0
- package/build/tools/vibe_pm/intent/types.js +70 -0
- package/build/tools/vibe_pm/intent/verifier.js +237 -0
- package/build/tools/vibe_pm/kce/doc_usage.js +51 -0
- package/build/tools/vibe_pm/kce/on_finalize.js +11 -0
- package/build/tools/vibe_pm/kce/preflight.js +232 -0
- package/build/tools/vibe_pm/local_memory.js +26 -0
- package/build/tools/vibe_pm/memory_status.js +82 -0
- package/build/tools/vibe_pm/memory_sync.js +134 -0
- package/build/tools/vibe_pm/modules/decision_snapshot.js +29 -0
- package/build/tools/vibe_pm/modules/ensure.js +100 -0
- package/build/tools/vibe_pm/modules/fingerprint.js +30 -0
- package/build/tools/vibe_pm/modules/fix_dependencies.js +394 -0
- package/build/tools/vibe_pm/modules/planning_v1.js +110 -0
- package/build/tools/vibe_pm/modules/repo_context.js +56 -0
- package/build/tools/vibe_pm/modules/research_v1.js +114 -0
- package/build/tools/vibe_pm/modules/skills_v1.js +100 -0
- package/build/tools/vibe_pm/pm_language.js +222 -0
- package/build/tools/vibe_pm/repair_plan.js +199 -0
- package/build/tools/vibe_pm/run_app.js +597 -0
- package/build/tools/vibe_pm/run_app_podman.js +64 -0
- package/build/tools/vibe_pm/scaffold.js +550 -0
- package/build/tools/vibe_pm/search_oss.js +124 -0
- package/build/tools/vibe_pm/status.js +153 -0
- package/build/tools/vibe_pm/submit_decision.js +87 -0
- package/build/tools/vibe_pm/system_design/issue_mapping.js +47 -0
- package/build/tools/vibe_pm/system_design/rulebook.js +112 -0
- package/build/tools/vibe_pm/system_design/semgrep.js +132 -0
- package/build/tools/vibe_pm/types.js +229 -0
- package/build/tools/vibe_pm/undo_last_task.js +163 -0
- package/build/tools/vibe_pm/update.js +146 -0
- package/build/tools/vibe_pm/zoekt_evidence.js +96 -0
- package/build/tools.js +269 -0
- package/build/version-check.js +239 -0
- package/build/vibe-cli.js +631 -0
- package/package.json +76 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { homedir } from 'os';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
4
|
+
import { existsSync } from 'fs';
|
|
5
|
+
import { AuthTokenFileSchema } from '../generated/auth_token_file.js';
|
|
6
|
+
const CONFIG_DIR = join(homedir(), '.vibe-pm');
|
|
7
|
+
const DEFAULT_TOKEN_FILE = join(CONFIG_DIR, 'auth-token.json');
|
|
8
|
+
export class TokenCache {
|
|
9
|
+
memoryCache = null;
|
|
10
|
+
resolveTokenFilePath() {
|
|
11
|
+
const override = (process.env.VIBECODE_AUTH_TOKEN_FILE || '').trim();
|
|
12
|
+
return override || DEFAULT_TOKEN_FILE;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get the cached token if it exists and is not expired
|
|
16
|
+
*/
|
|
17
|
+
async get() {
|
|
18
|
+
// Check memory cache first
|
|
19
|
+
if (this.memoryCache) {
|
|
20
|
+
if (this.isValid(this.memoryCache)) {
|
|
21
|
+
return this.memoryCache;
|
|
22
|
+
}
|
|
23
|
+
this.memoryCache = null;
|
|
24
|
+
}
|
|
25
|
+
// Try to load from disk
|
|
26
|
+
const diskCache = await this.loadFromDisk();
|
|
27
|
+
if (diskCache && this.isValid(diskCache)) {
|
|
28
|
+
this.memoryCache = diskCache;
|
|
29
|
+
return diskCache;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Save a token to the cache
|
|
35
|
+
*/
|
|
36
|
+
async set(cached) {
|
|
37
|
+
this.memoryCache = cached;
|
|
38
|
+
await this.saveToDisk(cached);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Clear the cached token
|
|
42
|
+
*/
|
|
43
|
+
async clear() {
|
|
44
|
+
this.memoryCache = null;
|
|
45
|
+
await this.deleteFromDisk();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Check if a cached token is still valid (not expired)
|
|
49
|
+
*/
|
|
50
|
+
isValid(cached) {
|
|
51
|
+
const now = Math.floor(Date.now() / 1000);
|
|
52
|
+
// Consider expired 60 seconds early to avoid edge cases
|
|
53
|
+
return cached.expiresAt > now + 60;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Check if the token needs refresh (expired or will expire soon)
|
|
57
|
+
*/
|
|
58
|
+
needsRefresh(cached, bufferSeconds = 300) {
|
|
59
|
+
const now = Math.floor(Date.now() / 1000);
|
|
60
|
+
return cached.expiresAt < now + bufferSeconds;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Load token from disk
|
|
64
|
+
*/
|
|
65
|
+
async loadFromDisk() {
|
|
66
|
+
try {
|
|
67
|
+
const tokenFile = this.resolveTokenFilePath();
|
|
68
|
+
if (!existsSync(tokenFile)) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const data = await readFile(tokenFile, 'utf-8');
|
|
72
|
+
const raw = JSON.parse(data);
|
|
73
|
+
const validated = AuthTokenFileSchema.safeParse(raw);
|
|
74
|
+
if (!validated.success) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
return validated.data;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Save token to disk
|
|
85
|
+
*/
|
|
86
|
+
async saveToDisk(cached) {
|
|
87
|
+
try {
|
|
88
|
+
// Ensure config directory exists
|
|
89
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
90
|
+
await mkdir(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
91
|
+
}
|
|
92
|
+
// Write with restricted permissions
|
|
93
|
+
const sealed = AuthTokenFileSchema.parse(cached);
|
|
94
|
+
const data = JSON.stringify(sealed, null, 2);
|
|
95
|
+
await writeFile(this.resolveTokenFilePath(), data, { mode: 0o600 });
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.error('Failed to save token to disk:', error);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Delete token from disk
|
|
103
|
+
*/
|
|
104
|
+
async deleteFromDisk() {
|
|
105
|
+
try {
|
|
106
|
+
const tokenFile = this.resolveTokenFilePath();
|
|
107
|
+
if (existsSync(tokenFile)) {
|
|
108
|
+
const { unlink } = await import('fs/promises');
|
|
109
|
+
await unlink(tokenFile);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Ignore errors when deleting
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get the token file path (for debugging)
|
|
118
|
+
*/
|
|
119
|
+
getTokenFilePath() {
|
|
120
|
+
return this.resolveTokenFilePath();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import * as jose from 'jose';
|
|
2
|
+
import { PUBLIC_KEY, JWT_ISSUER, JWT_AUDIENCE } from './public_key.js';
|
|
3
|
+
export class TokenVerifier {
|
|
4
|
+
publicKey = null;
|
|
5
|
+
/**
|
|
6
|
+
* Initialize the public key
|
|
7
|
+
*/
|
|
8
|
+
async getPublicKey() {
|
|
9
|
+
if (this.publicKey) {
|
|
10
|
+
return this.publicKey;
|
|
11
|
+
}
|
|
12
|
+
this.publicKey = await jose.importSPKI(PUBLIC_KEY, 'ES256');
|
|
13
|
+
return this.publicKey;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Verify a JWT token offline using the embedded public key
|
|
17
|
+
*/
|
|
18
|
+
async verify(token) {
|
|
19
|
+
try {
|
|
20
|
+
const key = await this.getPublicKey();
|
|
21
|
+
const { payload } = await jose.jwtVerify(token, key, {
|
|
22
|
+
issuer: JWT_ISSUER,
|
|
23
|
+
audience: JWT_AUDIENCE,
|
|
24
|
+
});
|
|
25
|
+
// Extract and validate claims
|
|
26
|
+
if (!payload.sub || !payload.ent || !payload.pol) {
|
|
27
|
+
return { success: false, error: 'invalid_token' };
|
|
28
|
+
}
|
|
29
|
+
const verified = {
|
|
30
|
+
subjectId: payload.sub,
|
|
31
|
+
entitlements: payload.ent,
|
|
32
|
+
policy: payload.pol,
|
|
33
|
+
issuedAt: payload.iat || 0,
|
|
34
|
+
expiresAt: payload.exp || 0,
|
|
35
|
+
};
|
|
36
|
+
return { success: true, token: verified };
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
// Map jose errors to our error types
|
|
40
|
+
if (error instanceof jose.errors.JWTExpired) {
|
|
41
|
+
return { success: false, error: 'expired_token' };
|
|
42
|
+
}
|
|
43
|
+
if (error instanceof jose.errors.JWSSignatureVerificationFailed) {
|
|
44
|
+
return { success: false, error: 'invalid_signature' };
|
|
45
|
+
}
|
|
46
|
+
if (error instanceof jose.errors.JWTClaimValidationFailed) {
|
|
47
|
+
const message = error.message || '';
|
|
48
|
+
if (message.includes('iss')) {
|
|
49
|
+
return { success: false, error: 'invalid_issuer' };
|
|
50
|
+
}
|
|
51
|
+
if (message.includes('aud')) {
|
|
52
|
+
return { success: false, error: 'invalid_audience' };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return { success: false, error: 'invalid_token' };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Quick check if a token is expired without full verification
|
|
60
|
+
*/
|
|
61
|
+
isExpired(token) {
|
|
62
|
+
try {
|
|
63
|
+
const decoded = jose.decodeJwt(token);
|
|
64
|
+
if (!decoded.exp) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
const now = Math.floor(Date.now() / 1000);
|
|
68
|
+
return decoded.exp < now;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get the time until token expires (in seconds)
|
|
76
|
+
* Returns 0 if expired or invalid
|
|
77
|
+
*/
|
|
78
|
+
getTimeUntilExpiry(token) {
|
|
79
|
+
try {
|
|
80
|
+
const decoded = jose.decodeJwt(token);
|
|
81
|
+
if (!decoded.exp) {
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
const now = Math.floor(Date.now() / 1000);
|
|
85
|
+
const remaining = decoded.exp - now;
|
|
86
|
+
return Math.max(0, remaining);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Decode token without verification (for debugging)
|
|
94
|
+
*/
|
|
95
|
+
decode(token) {
|
|
96
|
+
try {
|
|
97
|
+
return jose.decodeJwt(token);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// adapters/mcp-ts/src/bootstrap/doctor.ts
|
|
2
|
+
// Health check tool for installation status
|
|
3
|
+
import { ensureEngines, getEngineHealth, checkUpdates, validateCache } from "./installer.js";
|
|
4
|
+
import { ENGINE_SPECS } from "./registry.js";
|
|
5
|
+
/**
|
|
6
|
+
* Check engine installation status and return detailed health info
|
|
7
|
+
* Triggers installation if engines are missing or outdated
|
|
8
|
+
*/
|
|
9
|
+
export async function doctor() {
|
|
10
|
+
try {
|
|
11
|
+
// First, get current health status
|
|
12
|
+
const healthBefore = await getEngineHealth();
|
|
13
|
+
// Count issues
|
|
14
|
+
const summary = {
|
|
15
|
+
total: healthBefore.length,
|
|
16
|
+
ok: healthBefore.filter((h) => h.status === "ok").length,
|
|
17
|
+
needsUpdate: healthBefore.filter((h) => h.status === "needs_update").length,
|
|
18
|
+
missing: healthBefore.filter((h) => h.status === "missing").length,
|
|
19
|
+
corrupted: healthBefore.filter((h) => h.status === "corrupted").length
|
|
20
|
+
};
|
|
21
|
+
// If anything needs attention, trigger installation
|
|
22
|
+
if (summary.ok < summary.total) {
|
|
23
|
+
await ensureEngines();
|
|
24
|
+
}
|
|
25
|
+
// Get updated health status
|
|
26
|
+
const healthAfter = await getEngineHealth();
|
|
27
|
+
// Build result
|
|
28
|
+
const engines = {};
|
|
29
|
+
for (const h of healthAfter) {
|
|
30
|
+
engines[h.name] = {
|
|
31
|
+
version: h.version,
|
|
32
|
+
currentVersion: h.currentVersion,
|
|
33
|
+
path: h.path,
|
|
34
|
+
status: h.status
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
// Update summary
|
|
38
|
+
const summaryAfter = {
|
|
39
|
+
total: healthAfter.length,
|
|
40
|
+
ok: healthAfter.filter((h) => h.status === "ok").length,
|
|
41
|
+
needsUpdate: healthAfter.filter((h) => h.status === "needs_update").length,
|
|
42
|
+
missing: healthAfter.filter((h) => h.status === "missing").length,
|
|
43
|
+
corrupted: healthAfter.filter((h) => h.status === "corrupted").length
|
|
44
|
+
};
|
|
45
|
+
// Determine overall status
|
|
46
|
+
let status = "OK";
|
|
47
|
+
if (summaryAfter.missing > 0 || summaryAfter.corrupted > 0) {
|
|
48
|
+
status = "ERROR";
|
|
49
|
+
}
|
|
50
|
+
else if (summaryAfter.needsUpdate > 0) {
|
|
51
|
+
status = "NEEDS_UPDATE";
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
status,
|
|
55
|
+
engines,
|
|
56
|
+
summary: summaryAfter
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
return {
|
|
61
|
+
status: "ERROR",
|
|
62
|
+
engines: {},
|
|
63
|
+
summary: {
|
|
64
|
+
total: Object.keys(ENGINE_SPECS).length,
|
|
65
|
+
ok: 0,
|
|
66
|
+
needsUpdate: 0,
|
|
67
|
+
missing: Object.keys(ENGINE_SPECS).length,
|
|
68
|
+
corrupted: 0
|
|
69
|
+
},
|
|
70
|
+
error: e instanceof Error ? e.message : String(e)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Quick health check without triggering installation
|
|
76
|
+
*/
|
|
77
|
+
export async function healthCheck() {
|
|
78
|
+
const health = await getEngineHealth();
|
|
79
|
+
const allOk = health.every((h) => h.status === "ok");
|
|
80
|
+
return {
|
|
81
|
+
status: allOk ? "OK" : "NEEDS_ATTENTION",
|
|
82
|
+
engines: health
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Check for available updates
|
|
87
|
+
*/
|
|
88
|
+
export async function checkForUpdates() {
|
|
89
|
+
const updates = await checkUpdates();
|
|
90
|
+
const hasUpdates = Object.values(updates).some((u) => u.needsUpdate);
|
|
91
|
+
const result = {};
|
|
92
|
+
for (const [name, info] of Object.entries(updates)) {
|
|
93
|
+
if (info.needsUpdate) {
|
|
94
|
+
result[name] = {
|
|
95
|
+
current: info.current,
|
|
96
|
+
required: info.required
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
hasUpdates,
|
|
102
|
+
updates: result
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Validate cache integrity
|
|
107
|
+
*/
|
|
108
|
+
export async function validateCacheIntegrity() {
|
|
109
|
+
const cacheStatus = await validateCache();
|
|
110
|
+
const valid = Object.values(cacheStatus).every((c) => c.valid);
|
|
111
|
+
return {
|
|
112
|
+
valid,
|
|
113
|
+
engines: cacheStatus
|
|
114
|
+
};
|
|
115
|
+
}
|