@jagilber-org/index-server 1.19.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/CHANGELOG.md +1218 -0
- package/CODE_OF_CONDUCT.md +49 -0
- package/CONTRIBUTING.md +75 -0
- package/LICENSE +21 -0
- package/README.md +523 -0
- package/SECURITY.md +50 -0
- package/dist/config/configUtils.d.ts +11 -0
- package/dist/config/configUtils.js +87 -0
- package/dist/config/dashboardConfig.d.ts +45 -0
- package/dist/config/dashboardConfig.js +63 -0
- package/dist/config/defaultValues.d.ts +61 -0
- package/dist/config/defaultValues.js +70 -0
- package/dist/config/dirConstants.d.ts +17 -0
- package/dist/config/dirConstants.js +28 -0
- package/dist/config/featureConfig.d.ts +61 -0
- package/dist/config/featureConfig.js +121 -0
- package/dist/config/runtimeConfig.d.ts +145 -0
- package/dist/config/runtimeConfig.js +334 -0
- package/dist/config/serverConfig.d.ts +90 -0
- package/dist/config/serverConfig.js +164 -0
- package/dist/dashboard/analytics/AnalyticsEngine.d.ts +142 -0
- package/dist/dashboard/analytics/AnalyticsEngine.js +373 -0
- package/dist/dashboard/analytics/BusinessIntelligence.d.ts +187 -0
- package/dist/dashboard/analytics/BusinessIntelligence.js +594 -0
- package/dist/dashboard/client/admin.html +2150 -0
- package/dist/dashboard/client/chunks/mermaid-layout-elk.esm.min/chunk-SP2CHFBE.mjs +1 -0
- package/dist/dashboard/client/chunks/mermaid-layout-elk.esm.min/render-T6MDALS3.mjs +27 -0
- package/dist/dashboard/client/css/admin.css +1466 -0
- package/dist/dashboard/client/js/admin.boot.js +359 -0
- package/dist/dashboard/client/js/admin.config.js +196 -0
- package/dist/dashboard/client/js/admin.embeddings.js +425 -0
- package/dist/dashboard/client/js/admin.graph.js +583 -0
- package/dist/dashboard/client/js/admin.instances.js +120 -0
- package/dist/dashboard/client/js/admin.instructions.js +552 -0
- package/dist/dashboard/client/js/admin.logs.js +113 -0
- package/dist/dashboard/client/js/admin.maintenance.js +354 -0
- package/dist/dashboard/client/js/admin.messaging.js +635 -0
- package/dist/dashboard/client/js/admin.monitor.js +181 -0
- package/dist/dashboard/client/js/admin.overview.js +221 -0
- package/dist/dashboard/client/js/admin.performance.js +61 -0
- package/dist/dashboard/client/js/admin.sessions.js +293 -0
- package/dist/dashboard/client/js/admin.sqlite.js +366 -0
- package/dist/dashboard/client/js/admin.utils.js +49 -0
- package/dist/dashboard/client/js/chart.umd.js +14 -0
- package/dist/dashboard/client/js/elk.bundled.js +6696 -0
- package/dist/dashboard/client/js/marked.umd.js +74 -0
- package/dist/dashboard/client/js/mermaid.min.js +3022 -0
- package/dist/dashboard/client/mermaid-layout-elk.esm.min.mjs +1 -0
- package/dist/dashboard/export/DataExporter.d.ts +169 -0
- package/dist/dashboard/export/DataExporter.js +737 -0
- package/dist/dashboard/export/exporters/csvExporter.d.ts +11 -0
- package/dist/dashboard/export/exporters/csvExporter.js +46 -0
- package/dist/dashboard/export/exporters/exportTypes.d.ts +89 -0
- package/dist/dashboard/export/exporters/exportTypes.js +5 -0
- package/dist/dashboard/export/exporters/jsonExporter.d.ts +7 -0
- package/dist/dashboard/export/exporters/jsonExporter.js +22 -0
- package/dist/dashboard/export/exporters/xmlExporter.d.ts +17 -0
- package/dist/dashboard/export/exporters/xmlExporter.js +175 -0
- package/dist/dashboard/integration/APIIntegration.d.ts +41 -0
- package/dist/dashboard/integration/APIIntegration.js +95 -0
- package/dist/dashboard/security/SecurityMonitor.d.ts +167 -0
- package/dist/dashboard/security/SecurityMonitor.js +559 -0
- package/dist/dashboard/server/AdminPanel.d.ts +183 -0
- package/dist/dashboard/server/AdminPanel.js +792 -0
- package/dist/dashboard/server/AdminPanelConfig.d.ts +42 -0
- package/dist/dashboard/server/AdminPanelConfig.js +80 -0
- package/dist/dashboard/server/AdminPanelState.d.ts +47 -0
- package/dist/dashboard/server/AdminPanelState.js +214 -0
- package/dist/dashboard/server/ApiRoutes.d.ts +17 -0
- package/dist/dashboard/server/ApiRoutes.js +149 -0
- package/dist/dashboard/server/DashboardServer.d.ts +49 -0
- package/dist/dashboard/server/DashboardServer.js +159 -0
- package/dist/dashboard/server/FileMetricsStorage.d.ts +49 -0
- package/dist/dashboard/server/FileMetricsStorage.js +195 -0
- package/dist/dashboard/server/HttpTransport.d.ts +23 -0
- package/dist/dashboard/server/HttpTransport.js +116 -0
- package/dist/dashboard/server/InstanceManager.d.ts +53 -0
- package/dist/dashboard/server/InstanceManager.js +284 -0
- package/dist/dashboard/server/KnowledgeStore.d.ts +35 -0
- package/dist/dashboard/server/KnowledgeStore.js +105 -0
- package/dist/dashboard/server/LeaderElection.d.ts +81 -0
- package/dist/dashboard/server/LeaderElection.js +268 -0
- package/dist/dashboard/server/MetricsCollector.d.ts +200 -0
- package/dist/dashboard/server/MetricsCollector.js +803 -0
- package/dist/dashboard/server/SessionPersistenceManager.d.ts +88 -0
- package/dist/dashboard/server/SessionPersistenceManager.js +457 -0
- package/dist/dashboard/server/ThinClient.d.ts +64 -0
- package/dist/dashboard/server/ThinClient.js +237 -0
- package/dist/dashboard/server/WebSocketManager.d.ts +161 -0
- package/dist/dashboard/server/WebSocketManager.js +463 -0
- package/dist/dashboard/server/httpLifecycle.d.ts +17 -0
- package/dist/dashboard/server/httpLifecycle.js +35 -0
- package/dist/dashboard/server/legacyDashboardHtml.d.ts +9 -0
- package/dist/dashboard/server/legacyDashboardHtml.js +618 -0
- package/dist/dashboard/server/legacyDashboardStyles.d.ts +5 -0
- package/dist/dashboard/server/legacyDashboardStyles.js +490 -0
- package/dist/dashboard/server/metricsAggregation.d.ts +252 -0
- package/dist/dashboard/server/metricsAggregation.js +206 -0
- package/dist/dashboard/server/metricsSerializer.d.ts +25 -0
- package/dist/dashboard/server/metricsSerializer.js +195 -0
- package/dist/dashboard/server/routes/admin.routes.d.ts +16 -0
- package/dist/dashboard/server/routes/admin.routes.js +596 -0
- package/dist/dashboard/server/routes/alerts.routes.d.ts +7 -0
- package/dist/dashboard/server/routes/alerts.routes.js +93 -0
- package/dist/dashboard/server/routes/api.feedback.routes.d.ts +73 -0
- package/dist/dashboard/server/routes/api.feedback.routes.js +171 -0
- package/dist/dashboard/server/routes/api.instructions.routes.d.ts +101 -0
- package/dist/dashboard/server/routes/api.instructions.routes.js +213 -0
- package/dist/dashboard/server/routes/api.usage.routes.d.ts +57 -0
- package/dist/dashboard/server/routes/api.usage.routes.js +374 -0
- package/dist/dashboard/server/routes/embeddings.routes.d.ts +6 -0
- package/dist/dashboard/server/routes/embeddings.routes.js +246 -0
- package/dist/dashboard/server/routes/graph.routes.d.ts +6 -0
- package/dist/dashboard/server/routes/graph.routes.js +280 -0
- package/dist/dashboard/server/routes/index.d.ts +38 -0
- package/dist/dashboard/server/routes/index.js +194 -0
- package/dist/dashboard/server/routes/instances.routes.d.ts +6 -0
- package/dist/dashboard/server/routes/instances.routes.js +35 -0
- package/dist/dashboard/server/routes/instructions.routes.d.ts +8 -0
- package/dist/dashboard/server/routes/instructions.routes.js +336 -0
- package/dist/dashboard/server/routes/knowledge.routes.d.ts +6 -0
- package/dist/dashboard/server/routes/knowledge.routes.js +82 -0
- package/dist/dashboard/server/routes/logs.routes.d.ts +6 -0
- package/dist/dashboard/server/routes/logs.routes.js +164 -0
- package/dist/dashboard/server/routes/messaging.routes.d.ts +16 -0
- package/dist/dashboard/server/routes/messaging.routes.js +293 -0
- package/dist/dashboard/server/routes/metrics.routes.d.ts +10 -0
- package/dist/dashboard/server/routes/metrics.routes.js +346 -0
- package/dist/dashboard/server/routes/scripts.routes.d.ts +9 -0
- package/dist/dashboard/server/routes/scripts.routes.js +84 -0
- package/dist/dashboard/server/routes/sqlite.routes.d.ts +9 -0
- package/dist/dashboard/server/routes/sqlite.routes.js +569 -0
- package/dist/dashboard/server/routes/status.routes.d.ts +7 -0
- package/dist/dashboard/server/routes/status.routes.js +183 -0
- package/dist/dashboard/server/routes/synthetic.routes.d.ts +7 -0
- package/dist/dashboard/server/routes/synthetic.routes.js +195 -0
- package/dist/dashboard/server/routes/tools.routes.d.ts +6 -0
- package/dist/dashboard/server/routes/tools.routes.js +46 -0
- package/dist/dashboard/server/routes/usage.routes.d.ts +6 -0
- package/dist/dashboard/server/routes/usage.routes.js +25 -0
- package/dist/dashboard/server/wsInit.d.ts +16 -0
- package/dist/dashboard/server/wsInit.js +35 -0
- package/dist/externalClientLib.d.ts +1 -0
- package/dist/externalClientLib.js +2 -0
- package/dist/minimal/index.d.ts +1 -0
- package/dist/minimal/index.js +140 -0
- package/dist/models/SessionPersistence.d.ts +115 -0
- package/dist/models/SessionPersistence.js +66 -0
- package/dist/models/instruction.d.ts +45 -0
- package/dist/models/instruction.js +2 -0
- package/dist/perf/benchmark.d.ts +1 -0
- package/dist/perf/benchmark.js +50 -0
- package/dist/portableClientWrapper.d.ts +1 -0
- package/dist/portableClientWrapper.js +2 -0
- package/dist/schemas/index.d.ts +128 -0
- package/dist/schemas/index.js +371 -0
- package/dist/scripts/runPerformanceBaseline.d.ts +1 -0
- package/dist/scripts/runPerformanceBaseline.js +17 -0
- package/dist/server/handshakeManager.d.ts +25 -0
- package/dist/server/handshakeManager.js +472 -0
- package/dist/server/index-server.d.ts +56 -0
- package/dist/server/index-server.js +822 -0
- package/dist/server/registry.d.ts +44 -0
- package/dist/server/registry.js +236 -0
- package/dist/server/sdkServer.d.ts +8 -0
- package/dist/server/sdkServer.js +299 -0
- package/dist/server/shutdownGuard.d.ts +41 -0
- package/dist/server/shutdownGuard.js +52 -0
- package/dist/server/thin-client.d.ts +22 -0
- package/dist/server/thin-client.js +111 -0
- package/dist/server/transport.d.ts +41 -0
- package/dist/server/transport.js +312 -0
- package/dist/server/transportFactory.d.ts +21 -0
- package/dist/server/transportFactory.js +429 -0
- package/dist/services/atomicFs.d.ts +22 -0
- package/dist/services/atomicFs.js +103 -0
- package/dist/services/auditLog.d.ts +38 -0
- package/dist/services/auditLog.js +142 -0
- package/dist/services/autoBackup.d.ts +14 -0
- package/dist/services/autoBackup.js +171 -0
- package/dist/services/autoSplit.d.ts +32 -0
- package/dist/services/autoSplit.js +113 -0
- package/dist/services/backupZip.d.ts +25 -0
- package/dist/services/backupZip.js +110 -0
- package/dist/services/bootstrapGating.d.ts +123 -0
- package/dist/services/bootstrapGating.js +221 -0
- package/dist/services/canonical.d.ts +23 -0
- package/dist/services/canonical.js +65 -0
- package/dist/services/categoryRules.d.ts +7 -0
- package/dist/services/categoryRules.js +37 -0
- package/dist/services/classificationService.d.ts +42 -0
- package/dist/services/classificationService.js +168 -0
- package/dist/services/embeddingService.d.ts +62 -0
- package/dist/services/embeddingService.js +259 -0
- package/dist/services/errors.d.ts +22 -0
- package/dist/services/errors.js +31 -0
- package/dist/services/featureFlags.d.ts +25 -0
- package/dist/services/featureFlags.js +89 -0
- package/dist/services/features.d.ts +13 -0
- package/dist/services/features.js +35 -0
- package/dist/services/handlers/instructions.add.d.ts +1 -0
- package/dist/services/handlers/instructions.add.js +496 -0
- package/dist/services/handlers/instructions.groom.d.ts +1 -0
- package/dist/services/handlers/instructions.groom.js +523 -0
- package/dist/services/handlers/instructions.import.d.ts +1 -0
- package/dist/services/handlers/instructions.import.js +173 -0
- package/dist/services/handlers/instructions.patch.d.ts +1 -0
- package/dist/services/handlers/instructions.patch.js +167 -0
- package/dist/services/handlers/instructions.query.d.ts +163 -0
- package/dist/services/handlers/instructions.query.js +522 -0
- package/dist/services/handlers/instructions.reload.d.ts +1 -0
- package/dist/services/handlers/instructions.reload.js +13 -0
- package/dist/services/handlers/instructions.remove.d.ts +1 -0
- package/dist/services/handlers/instructions.remove.js +118 -0
- package/dist/services/handlers/instructions.shared.d.ts +31 -0
- package/dist/services/handlers/instructions.shared.js +124 -0
- package/dist/services/handlers.activation.d.ts +1 -0
- package/dist/services/handlers.activation.js +203 -0
- package/dist/services/handlers.bootstrap.d.ts +1 -0
- package/dist/services/handlers.bootstrap.js +38 -0
- package/dist/services/handlers.dashboardConfig.d.ts +34 -0
- package/dist/services/handlers.dashboardConfig.js +108 -0
- package/dist/services/handlers.diagnostics.d.ts +1 -0
- package/dist/services/handlers.diagnostics.js +64 -0
- package/dist/services/handlers.feedback.d.ts +15 -0
- package/dist/services/handlers.feedback.js +378 -0
- package/dist/services/handlers.gates.d.ts +1 -0
- package/dist/services/handlers.gates.js +46 -0
- package/dist/services/handlers.graph.d.ts +53 -0
- package/dist/services/handlers.graph.js +231 -0
- package/dist/services/handlers.help.d.ts +1 -0
- package/dist/services/handlers.help.js +119 -0
- package/dist/services/handlers.instructionSchema.d.ts +1 -0
- package/dist/services/handlers.instructionSchema.js +227 -0
- package/dist/services/handlers.instructions.d.ts +8 -0
- package/dist/services/handlers.instructions.js +14 -0
- package/dist/services/handlers.instructionsDiagnostics.d.ts +1 -0
- package/dist/services/handlers.instructionsDiagnostics.js +14 -0
- package/dist/services/handlers.integrity.d.ts +1 -0
- package/dist/services/handlers.integrity.js +35 -0
- package/dist/services/handlers.manifest.d.ts +1 -0
- package/dist/services/handlers.manifest.js +24 -0
- package/dist/services/handlers.messaging.d.ts +12 -0
- package/dist/services/handlers.messaging.js +203 -0
- package/dist/services/handlers.metrics.d.ts +1 -0
- package/dist/services/handlers.metrics.js +43 -0
- package/dist/services/handlers.promote.d.ts +1 -0
- package/dist/services/handlers.promote.js +306 -0
- package/dist/services/handlers.prompt.d.ts +1 -0
- package/dist/services/handlers.prompt.js +7 -0
- package/dist/services/handlers.search.d.ts +69 -0
- package/dist/services/handlers.search.js +645 -0
- package/dist/services/handlers.testPrimitive.d.ts +1 -0
- package/dist/services/handlers.testPrimitive.js +5 -0
- package/dist/services/handlers.trace.d.ts +1 -0
- package/dist/services/handlers.trace.js +31 -0
- package/dist/services/handlers.usage.d.ts +1 -0
- package/dist/services/handlers.usage.js +11 -0
- package/dist/services/hotScore.d.ts +137 -0
- package/dist/services/hotScore.js +244 -0
- package/dist/services/indexContext.d.ts +117 -0
- package/dist/services/indexContext.js +968 -0
- package/dist/services/indexLoader.d.ts +44 -0
- package/dist/services/indexLoader.js +921 -0
- package/dist/services/indexRepository.d.ts +32 -0
- package/dist/services/indexRepository.js +71 -0
- package/dist/services/indexingService.d.ts +1 -0
- package/dist/services/indexingService.js +2 -0
- package/dist/services/instructions.dispatcher.d.ts +1 -0
- package/dist/services/instructions.dispatcher.js +231 -0
- package/dist/services/logPrefix.d.ts +1 -0
- package/dist/services/logPrefix.js +30 -0
- package/dist/services/logger.d.ts +52 -0
- package/dist/services/logger.js +268 -0
- package/dist/services/manifestManager.d.ts +82 -0
- package/dist/services/manifestManager.js +200 -0
- package/dist/services/messaging/agentMailbox.d.ts +60 -0
- package/dist/services/messaging/agentMailbox.js +353 -0
- package/dist/services/messaging/messagingPersistence.d.ts +20 -0
- package/dist/services/messaging/messagingPersistence.js +111 -0
- package/dist/services/messaging/messagingTypes.d.ts +150 -0
- package/dist/services/messaging/messagingTypes.js +66 -0
- package/dist/services/ownershipService.d.ts +1 -0
- package/dist/services/ownershipService.js +38 -0
- package/dist/services/performanceBaseline.d.ts +19 -0
- package/dist/services/performanceBaseline.js +210 -0
- package/dist/services/preflight.d.ts +12 -0
- package/dist/services/preflight.js +79 -0
- package/dist/services/promptReviewService.d.ts +44 -0
- package/dist/services/promptReviewService.js +101 -0
- package/dist/services/responseEnvelope.d.ts +6 -0
- package/dist/services/responseEnvelope.js +25 -0
- package/dist/services/seedBootstrap.d.ts +34 -0
- package/dist/services/seedBootstrap.js +427 -0
- package/dist/services/storage/factory.d.ts +17 -0
- package/dist/services/storage/factory.js +35 -0
- package/dist/services/storage/hashUtils.d.ts +11 -0
- package/dist/services/storage/hashUtils.js +35 -0
- package/dist/services/storage/index.d.ts +12 -0
- package/dist/services/storage/index.js +18 -0
- package/dist/services/storage/jsonFileStore.d.ts +32 -0
- package/dist/services/storage/jsonFileStore.js +241 -0
- package/dist/services/storage/migrationEngine.d.ts +35 -0
- package/dist/services/storage/migrationEngine.js +93 -0
- package/dist/services/storage/sqliteMessageStore.d.ts +53 -0
- package/dist/services/storage/sqliteMessageStore.js +146 -0
- package/dist/services/storage/sqliteSchema.d.ts +12 -0
- package/dist/services/storage/sqliteSchema.js +122 -0
- package/dist/services/storage/sqliteStore.d.ts +41 -0
- package/dist/services/storage/sqliteStore.js +339 -0
- package/dist/services/storage/sqliteUsageStore.d.ts +35 -0
- package/dist/services/storage/sqliteUsageStore.js +94 -0
- package/dist/services/storage/types.d.ts +171 -0
- package/dist/services/storage/types.js +12 -0
- package/dist/services/toolHandlers.d.ts +23 -0
- package/dist/services/toolHandlers.js +50 -0
- package/dist/services/toolRegistry.d.ts +20 -0
- package/dist/services/toolRegistry.js +490 -0
- package/dist/services/toolRegistry.zod.d.ts +10 -0
- package/dist/services/toolRegistry.zod.js +323 -0
- package/dist/services/tracing.d.ts +26 -0
- package/dist/services/tracing.js +260 -0
- package/dist/services/usageBuckets.d.ts +161 -0
- package/dist/services/usageBuckets.js +364 -0
- package/dist/services/validationService.d.ts +38 -0
- package/dist/services/validationService.js +125 -0
- package/dist/utils/BufferRing.d.ts +203 -0
- package/dist/utils/BufferRing.js +551 -0
- package/dist/utils/BufferRingExamples.d.ts +55 -0
- package/dist/utils/BufferRingExamples.js +188 -0
- package/dist/utils/envUtils.d.ts +42 -0
- package/dist/utils/envUtils.js +80 -0
- package/dist/utils/memoryMonitor.d.ts +83 -0
- package/dist/utils/memoryMonitor.js +275 -0
- package/dist/versioning/schemaVersion.d.ts +6 -0
- package/dist/versioning/schemaVersion.js +93 -0
- package/package.json +134 -0
- package/schemas/README.md +13 -0
- package/schemas/feedback-entry.schema.json +27 -0
- package/schemas/graph-export-v2.schema.json +60 -0
- package/schemas/index-server.code-schema.json +38477 -0
- package/schemas/instruction.schema.json +262 -0
- package/schemas/json-schema/SessionPersistence-persisted-admin-session.schema.json +54 -0
- package/schemas/json-schema/SessionPersistence-persisted-session-history-entry.schema.json +51 -0
- package/schemas/json-schema/SessionPersistence-persisted-web-socket-connection.schema.json +54 -0
- package/schemas/json-schema/SessionPersistence-session-persistence-config.schema.json +110 -0
- package/schemas/json-schema/SessionPersistence-session-persistence-data.schema.json +229 -0
- package/schemas/json-schema/SessionPersistence-session-persistence-manifest.schema.json +109 -0
- package/schemas/json-schema/SessionPersistence-session-persistence-metadata.schema.json +55 -0
- package/schemas/json-schema/instruction-audience-scope.schema.json +14 -0
- package/schemas/json-schema/instruction-content-type.schema.json +17 -0
- package/schemas/json-schema/instruction-instruction-entry.schema.json +206 -0
- package/schemas/json-schema/instruction-requirement-level.schema.json +16 -0
- package/schemas/manifest.json +78 -0
- package/schemas/manifest.schema.json +33 -0
- package/schemas/usage-batch.schema.json +16 -0
- package/schemas/usage-buckets.schema.json +30 -0
- package/schemas/usage-event.schema.json +17 -0
- package/scripts/copy-dashboard-assets.mjs +170 -0
- package/scripts/setup-hooks.cjs +28 -0
|
@@ -0,0 +1,792 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AdminPanel = void 0;
|
|
7
|
+
exports.getAdminPanel = getAdminPanel;
|
|
8
|
+
/**
|
|
9
|
+
* Index Server Admin - Enterprise Administration Interface
|
|
10
|
+
*
|
|
11
|
+
* Thin coordinator that delegates to:
|
|
12
|
+
* - AdminPanelConfig — server configuration rendering/serialization
|
|
13
|
+
* - AdminPanelState — admin session state management
|
|
14
|
+
*
|
|
15
|
+
* Retains direct responsibility for maintenance operations, backup/restore,
|
|
16
|
+
* system health monitoring, and admin statistics.
|
|
17
|
+
*/
|
|
18
|
+
const fs_1 = __importDefault(require("fs"));
|
|
19
|
+
const path_1 = __importDefault(require("path"));
|
|
20
|
+
const v8_1 = __importDefault(require("v8"));
|
|
21
|
+
const runtimeConfig_1 = require("../../config/runtimeConfig");
|
|
22
|
+
const MetricsCollector_1 = require("./MetricsCollector");
|
|
23
|
+
const indexContext_1 = require("../../services/indexContext");
|
|
24
|
+
const AdminPanelConfig_1 = require("./AdminPanelConfig");
|
|
25
|
+
const AdminPanelState_1 = require("./AdminPanelState");
|
|
26
|
+
const backupZip_1 = require("../../services/backupZip");
|
|
27
|
+
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
28
|
+
class AdminPanel {
|
|
29
|
+
panelConfig;
|
|
30
|
+
panelState;
|
|
31
|
+
maintenanceInfo;
|
|
32
|
+
indexStatsCache = null;
|
|
33
|
+
lastUptimeSeconds = 0;
|
|
34
|
+
get backupRoot() {
|
|
35
|
+
return (0, runtimeConfig_1.getRuntimeConfig)().dashboard.admin.backupsDir || path_1.default.join(process.cwd(), 'backups');
|
|
36
|
+
}
|
|
37
|
+
get instructionsRoot() {
|
|
38
|
+
return (0, runtimeConfig_1.getRuntimeConfig)().index.baseDir || path_1.default.join(process.cwd(), 'instructions');
|
|
39
|
+
}
|
|
40
|
+
// CPU tracking for leak detection
|
|
41
|
+
cpuHistory = [];
|
|
42
|
+
lastCpuUsage = null;
|
|
43
|
+
maxCpuHistoryEntries = 100;
|
|
44
|
+
// Memory tracking for leak detection
|
|
45
|
+
memoryHistory = [];
|
|
46
|
+
maxMemoryHistoryEntries = 100;
|
|
47
|
+
constructor() {
|
|
48
|
+
this.panelConfig = new AdminPanelConfig_1.AdminPanelConfig();
|
|
49
|
+
this.panelState = new AdminPanelState_1.AdminPanelState();
|
|
50
|
+
this.maintenanceInfo = {
|
|
51
|
+
lastBackup: null,
|
|
52
|
+
nextScheduledMaintenance: null,
|
|
53
|
+
maintenanceMode: false,
|
|
54
|
+
systemHealth: { status: 'healthy', issues: [], recommendations: [] }
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// ── Config delegation ──────────────────────────────────────────────────────────────
|
|
58
|
+
getAdminConfig() {
|
|
59
|
+
return this.panelConfig.getAdminConfig();
|
|
60
|
+
}
|
|
61
|
+
updateAdminConfig(updates) {
|
|
62
|
+
return this.panelConfig.updateAdminConfig(updates);
|
|
63
|
+
}
|
|
64
|
+
// ── Session state delegation ────────────────────────────────────────────────────────
|
|
65
|
+
getActiveSessions() {
|
|
66
|
+
return this.panelState.getActiveSessions(this.panelConfig.sessionTimeout);
|
|
67
|
+
}
|
|
68
|
+
createAdminSession(userId, ipAddress, userAgent) {
|
|
69
|
+
return this.panelState.createAdminSession(userId, ipAddress, userAgent);
|
|
70
|
+
}
|
|
71
|
+
terminateSession(sessionId) {
|
|
72
|
+
return this.panelState.terminateSession(sessionId);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get system maintenance information
|
|
76
|
+
*/
|
|
77
|
+
getMaintenanceInfo() {
|
|
78
|
+
// Update system health
|
|
79
|
+
this.updateSystemHealth();
|
|
80
|
+
return JSON.parse(JSON.stringify(this.maintenanceInfo));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Set maintenance mode
|
|
84
|
+
*/
|
|
85
|
+
setMaintenanceMode(enabled, message) {
|
|
86
|
+
try {
|
|
87
|
+
this.maintenanceInfo.maintenanceMode = enabled;
|
|
88
|
+
if (enabled) {
|
|
89
|
+
process.stderr.write(`[admin] Maintenance mode ENABLED${message ? `: ${message}` : ''}\n`);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
process.stderr.write(`[admin] Maintenance mode DISABLED\n`);
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
success: true,
|
|
96
|
+
message: `Maintenance mode ${enabled ? 'enabled' : 'disabled'}`
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
return {
|
|
101
|
+
success: false,
|
|
102
|
+
message: `Failed to set maintenance mode: ${error instanceof Error ? error.message : String(error)}`
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Perform system backup
|
|
108
|
+
*/
|
|
109
|
+
async performBackup() {
|
|
110
|
+
try {
|
|
111
|
+
const backupRoot = this.backupRoot;
|
|
112
|
+
if (!fs_1.default.existsSync(backupRoot))
|
|
113
|
+
fs_1.default.mkdirSync(backupRoot, { recursive: true });
|
|
114
|
+
const now = new Date();
|
|
115
|
+
const iso = now.toISOString();
|
|
116
|
+
const baseTs = iso.replace(/[-:]/g, '').replace(/\..+/, '');
|
|
117
|
+
const ms = String(now.getMilliseconds()).padStart(3, '0');
|
|
118
|
+
const backupId = `backup_${baseTs}_${ms}`;
|
|
119
|
+
const zipPath = path_1.default.join(backupRoot, `${backupId}.zip`);
|
|
120
|
+
const instructionsDir = this.instructionsRoot;
|
|
121
|
+
let fileCount = 0;
|
|
122
|
+
if (fs_1.default.existsSync(instructionsDir)) {
|
|
123
|
+
fileCount = fs_1.default.readdirSync(instructionsDir).filter(f => f.toLowerCase().endsWith('.json')).length;
|
|
124
|
+
}
|
|
125
|
+
const manifest = {
|
|
126
|
+
backupId,
|
|
127
|
+
createdAt: now.toISOString(),
|
|
128
|
+
instructionCount: fileCount,
|
|
129
|
+
schemaVersion: this.indexStatsCache?.schemaVersion || 'unknown',
|
|
130
|
+
};
|
|
131
|
+
(0, backupZip_1.createZipBackupWithManifest)(instructionsDir, zipPath, manifest);
|
|
132
|
+
this.maintenanceInfo.lastBackup = new Date();
|
|
133
|
+
process.stderr.write(`[admin] System backup completed: ${backupId}.zip (${fileCount} files)\n`);
|
|
134
|
+
return { success: true, message: 'System backup completed successfully', backupId, files: fileCount };
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
return {
|
|
138
|
+
success: false,
|
|
139
|
+
message: `Backup failed: ${error instanceof Error ? error.message : String(error)}`
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
listBackups() {
|
|
144
|
+
const backupRoot = this.backupRoot;
|
|
145
|
+
if (!fs_1.default.existsSync(backupRoot))
|
|
146
|
+
return [];
|
|
147
|
+
const results = [];
|
|
148
|
+
for (const entry of fs_1.default.readdirSync(backupRoot)) {
|
|
149
|
+
const full = path_1.default.join(backupRoot, entry);
|
|
150
|
+
try {
|
|
151
|
+
const stat = fs_1.default.statSync(full);
|
|
152
|
+
if ((0, backupZip_1.isZipBackup)(entry) && stat.isFile()) {
|
|
153
|
+
// Zip backup
|
|
154
|
+
const id = entry.replace(/\.zip$/i, '');
|
|
155
|
+
let createdAt = new Date(stat.mtime).toISOString();
|
|
156
|
+
let instructionCount = 0;
|
|
157
|
+
let schemaVersion;
|
|
158
|
+
const manifest = (0, backupZip_1.readZipManifest)(full);
|
|
159
|
+
if (manifest) {
|
|
160
|
+
createdAt = manifest.createdAt || createdAt;
|
|
161
|
+
instructionCount = manifest.instructionCount || 0;
|
|
162
|
+
schemaVersion = manifest.schemaVersion;
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
instructionCount = (0, backupZip_1.listZipInstructionFiles)(full).length;
|
|
166
|
+
}
|
|
167
|
+
results.push({ id, createdAt, instructionCount, schemaVersion, sizeBytes: stat.size });
|
|
168
|
+
}
|
|
169
|
+
else if (stat.isDirectory()) {
|
|
170
|
+
// Legacy directory backup
|
|
171
|
+
const manifestPath = path_1.default.join(full, 'manifest.json');
|
|
172
|
+
let createdAt = new Date(stat.mtime).toISOString();
|
|
173
|
+
let instructionCount = 0;
|
|
174
|
+
let schemaVersion;
|
|
175
|
+
if (fs_1.default.existsSync(manifestPath)) {
|
|
176
|
+
try {
|
|
177
|
+
const mf = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf-8'));
|
|
178
|
+
createdAt = mf.createdAt || createdAt;
|
|
179
|
+
instructionCount = mf.instructionCount || 0;
|
|
180
|
+
schemaVersion = mf.schemaVersion;
|
|
181
|
+
}
|
|
182
|
+
catch { /* ignore */ }
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
instructionCount = fs_1.default.readdirSync(full).filter(f => f.toLowerCase().endsWith('.json')).length;
|
|
186
|
+
}
|
|
187
|
+
const sizeBytes = fs_1.default.readdirSync(full).reduce((sum, f) => {
|
|
188
|
+
try {
|
|
189
|
+
return sum + fs_1.default.statSync(path_1.default.join(full, f)).size;
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return sum;
|
|
193
|
+
}
|
|
194
|
+
}, 0);
|
|
195
|
+
results.push({ id: entry, createdAt, instructionCount, schemaVersion, sizeBytes });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
catch { /* ignore individual entry errors */ }
|
|
199
|
+
}
|
|
200
|
+
results.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
201
|
+
return results;
|
|
202
|
+
}
|
|
203
|
+
restoreBackup(backupId) {
|
|
204
|
+
try {
|
|
205
|
+
if (!backupId)
|
|
206
|
+
return { success: false, message: 'backupId required' };
|
|
207
|
+
const backupRoot = this.backupRoot;
|
|
208
|
+
const zipPath = path_1.default.join(backupRoot, `${backupId}.zip`);
|
|
209
|
+
const backupDir = path_1.default.join(backupRoot, backupId);
|
|
210
|
+
const isZip = fs_1.default.existsSync(zipPath);
|
|
211
|
+
const isDir = fs_1.default.existsSync(backupDir) && fs_1.default.statSync(backupDir).isDirectory();
|
|
212
|
+
if (!isZip && !isDir)
|
|
213
|
+
return { success: false, message: `Backup not found: ${backupId}` };
|
|
214
|
+
const instructionsDir = this.instructionsRoot;
|
|
215
|
+
if (!fs_1.default.existsSync(instructionsDir))
|
|
216
|
+
fs_1.default.mkdirSync(instructionsDir, { recursive: true });
|
|
217
|
+
// Pre-restore safety backup (as zip)
|
|
218
|
+
const existing = fs_1.default.readdirSync(instructionsDir).filter(f => f.toLowerCase().endsWith('.json'));
|
|
219
|
+
if (existing.length) {
|
|
220
|
+
const safetyId = `pre_restore_${Date.now()}`;
|
|
221
|
+
const safetyZipPath = path_1.default.join(backupRoot, `${safetyId}.zip`);
|
|
222
|
+
(0, backupZip_1.createZipBackupWithManifest)(instructionsDir, safetyZipPath, {
|
|
223
|
+
type: 'pre-restore',
|
|
224
|
+
createdAt: new Date().toISOString(),
|
|
225
|
+
source: backupId,
|
|
226
|
+
originalCount: existing.length,
|
|
227
|
+
});
|
|
228
|
+
process.stderr.write(`[admin] Pre-restore safety backup created: ${safetyId}.zip\n`);
|
|
229
|
+
}
|
|
230
|
+
let restored = 0;
|
|
231
|
+
if (isZip) {
|
|
232
|
+
restored = (0, backupZip_1.extractZipBackup)(zipPath, instructionsDir);
|
|
233
|
+
// Don't count manifest.json as a restored instruction
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
// Legacy directory restore
|
|
237
|
+
for (const f of fs_1.default.readdirSync(backupDir)) {
|
|
238
|
+
if (f.toLowerCase().endsWith('.json') && f !== 'manifest.json') {
|
|
239
|
+
fs_1.default.copyFileSync(path_1.default.join(backupDir, f), path_1.default.join(instructionsDir, f));
|
|
240
|
+
restored++;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
this.indexStatsCache = null;
|
|
245
|
+
process.stderr.write(`[admin] Restored backup ${backupId} (${restored} instruction files)\n`);
|
|
246
|
+
return { success: true, message: `Backup ${backupId} restored`, restored };
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
return { success: false, message: `Restore failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/** Delete a backup zip or directory (safety checks on name) */
|
|
253
|
+
deleteBackup(backupId) {
|
|
254
|
+
try {
|
|
255
|
+
if (!backupId)
|
|
256
|
+
return { success: false, message: 'backupId required' };
|
|
257
|
+
const backupRoot = this.backupRoot;
|
|
258
|
+
// Check for zip first, then legacy directory
|
|
259
|
+
const zipPath = path_1.default.join(backupRoot, `${backupId}.zip`);
|
|
260
|
+
const dirPath = path_1.default.join(backupRoot, backupId);
|
|
261
|
+
const hasZip = fs_1.default.existsSync(zipPath);
|
|
262
|
+
const hasDir = fs_1.default.existsSync(dirPath) && fs_1.default.statSync(dirPath).isDirectory();
|
|
263
|
+
if (!hasZip && !hasDir)
|
|
264
|
+
return { success: false, message: `Backup not found: ${backupId}` };
|
|
265
|
+
if (!/^backup_|^instructions-|^pre_restore_|^auto-backup-/.test(backupId)) {
|
|
266
|
+
return { success: false, message: 'Refusing to delete unexpected backup name' };
|
|
267
|
+
}
|
|
268
|
+
if (hasZip)
|
|
269
|
+
fs_1.default.unlinkSync(zipPath);
|
|
270
|
+
if (hasDir)
|
|
271
|
+
fs_1.default.rmSync(dirPath, { recursive: true, force: true });
|
|
272
|
+
process.stderr.write(`[admin] Deleted backup ${backupId}\n`);
|
|
273
|
+
return { success: true, message: `Backup ${backupId} deleted`, removed: true };
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
return { success: false, message: `Delete failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/** Prune backups keeping newest N (by createdAt / mtime). Returns count pruned. */
|
|
280
|
+
pruneBackups(retain) {
|
|
281
|
+
try {
|
|
282
|
+
if (retain < 0)
|
|
283
|
+
return { success: false, message: 'retain must be >= 0' };
|
|
284
|
+
const backupRoot = this.backupRoot;
|
|
285
|
+
if (!fs_1.default.existsSync(backupRoot))
|
|
286
|
+
return { success: true, message: 'No backups to prune', pruned: 0 };
|
|
287
|
+
const entries = fs_1.default.readdirSync(backupRoot)
|
|
288
|
+
.map(name => {
|
|
289
|
+
const full = path_1.default.join(backupRoot, name);
|
|
290
|
+
// Derive the logical ID (strip .zip extension)
|
|
291
|
+
const id = name.endsWith('.zip') ? name.replace(/\.zip$/i, '') : name;
|
|
292
|
+
return { name, id, full };
|
|
293
|
+
})
|
|
294
|
+
.filter(d => {
|
|
295
|
+
try {
|
|
296
|
+
const stat = fs_1.default.statSync(d.full);
|
|
297
|
+
if (d.name.endsWith('.zip') && stat.isFile())
|
|
298
|
+
return /^backup_|^instructions-|^pre_restore_|^auto-backup-/.test(d.id);
|
|
299
|
+
if (stat.isDirectory())
|
|
300
|
+
return /^backup_|^instructions-|^pre_restore_|^auto-backup-/.test(d.id);
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
// sort newest first by mtime
|
|
308
|
+
entries.sort((a, b) => {
|
|
309
|
+
try {
|
|
310
|
+
return fs_1.default.statSync(b.full).mtime.getTime() - fs_1.default.statSync(a.full).mtime.getTime();
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
return 0;
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
if (retain === 0) {
|
|
317
|
+
let prunedAll = 0;
|
|
318
|
+
for (const d of entries) {
|
|
319
|
+
try {
|
|
320
|
+
if (d.name.endsWith('.zip'))
|
|
321
|
+
fs_1.default.unlinkSync(d.full);
|
|
322
|
+
else
|
|
323
|
+
fs_1.default.rmSync(d.full, { recursive: true, force: true });
|
|
324
|
+
prunedAll++;
|
|
325
|
+
}
|
|
326
|
+
catch { /* ignore */ }
|
|
327
|
+
}
|
|
328
|
+
process.stderr.write(`[admin] Pruned all backups (${prunedAll})\n`);
|
|
329
|
+
return { success: true, message: `Pruned ${prunedAll} backups`, pruned: prunedAll };
|
|
330
|
+
}
|
|
331
|
+
const toDelete = entries.slice(retain);
|
|
332
|
+
let pruned = 0;
|
|
333
|
+
for (const d of toDelete) {
|
|
334
|
+
try {
|
|
335
|
+
if (d.name.endsWith('.zip'))
|
|
336
|
+
fs_1.default.unlinkSync(d.full);
|
|
337
|
+
else
|
|
338
|
+
fs_1.default.rmSync(d.full, { recursive: true, force: true });
|
|
339
|
+
pruned++;
|
|
340
|
+
}
|
|
341
|
+
catch { /* ignore */ }
|
|
342
|
+
}
|
|
343
|
+
process.stderr.write(`[admin] Pruned ${pruned} backup(s); retained ${entries.length - pruned}\n`);
|
|
344
|
+
return { success: true, message: `Pruned ${pruned} backups (retained ${entries.length - pruned})`, pruned };
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
return { success: false, message: `Prune failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/** Export a backup — returns the zip file path for streaming, or falls back to JSON bundle for legacy dirs */
|
|
351
|
+
exportBackup(backupId) {
|
|
352
|
+
try {
|
|
353
|
+
if (!backupId)
|
|
354
|
+
return { success: false, message: 'backupId required' };
|
|
355
|
+
const zipPath = path_1.default.join(this.backupRoot, `${backupId}.zip`);
|
|
356
|
+
if (fs_1.default.existsSync(zipPath) && fs_1.default.statSync(zipPath).isFile()) {
|
|
357
|
+
return { success: true, message: 'Export ready', zipPath };
|
|
358
|
+
}
|
|
359
|
+
// Legacy directory fallback
|
|
360
|
+
const backupDir = path_1.default.join(this.backupRoot, backupId);
|
|
361
|
+
if (!fs_1.default.existsSync(backupDir) || !fs_1.default.statSync(backupDir).isDirectory())
|
|
362
|
+
return { success: false, message: `Backup not found: ${backupId}` };
|
|
363
|
+
let manifest = {};
|
|
364
|
+
const manifestPath = path_1.default.join(backupDir, 'manifest.json');
|
|
365
|
+
if (fs_1.default.existsSync(manifestPath)) {
|
|
366
|
+
try {
|
|
367
|
+
manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf-8'));
|
|
368
|
+
}
|
|
369
|
+
catch { /* ignore */ }
|
|
370
|
+
}
|
|
371
|
+
const files = {};
|
|
372
|
+
for (const f of fs_1.default.readdirSync(backupDir)) {
|
|
373
|
+
if (f.toLowerCase().endsWith('.json') && f !== 'manifest.json') {
|
|
374
|
+
try {
|
|
375
|
+
files[f] = JSON.parse(fs_1.default.readFileSync(path_1.default.join(backupDir, f), 'utf-8'));
|
|
376
|
+
}
|
|
377
|
+
catch { /* skip corrupt */ }
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return { success: true, message: 'Export ready', bundle: { manifest, files } };
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
return { success: false, message: `Export failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/** Import a backup from a JSON bundle uploaded by the client — creates a zip */
|
|
387
|
+
importBackup(bundle) {
|
|
388
|
+
try {
|
|
389
|
+
if (!bundle || typeof bundle !== 'object' || !bundle.files || typeof bundle.files !== 'object') {
|
|
390
|
+
return { success: false, message: 'Invalid bundle: must contain a "files" object' };
|
|
391
|
+
}
|
|
392
|
+
const now = new Date();
|
|
393
|
+
const baseTs = now.toISOString().replace(/[-:]/g, '').replace(/\..+/, '');
|
|
394
|
+
const ms = String(now.getMilliseconds()).padStart(3, '0');
|
|
395
|
+
const backupId = `backup_${baseTs}_${ms}`;
|
|
396
|
+
const zipPath = path_1.default.join(this.backupRoot, `${backupId}.zip`);
|
|
397
|
+
if (!fs_1.default.existsSync(this.backupRoot))
|
|
398
|
+
fs_1.default.mkdirSync(this.backupRoot, { recursive: true });
|
|
399
|
+
const zip = new adm_zip_1.default();
|
|
400
|
+
let written = 0;
|
|
401
|
+
for (const [name, content] of Object.entries(bundle.files)) {
|
|
402
|
+
if (typeof name !== 'string' || !name.toLowerCase().endsWith('.json'))
|
|
403
|
+
continue;
|
|
404
|
+
const safeName = path_1.default.basename(name);
|
|
405
|
+
if (safeName !== name || safeName.includes('..'))
|
|
406
|
+
continue; // path traversal guard
|
|
407
|
+
zip.addFile(safeName, Buffer.from(JSON.stringify(content, null, 2)));
|
|
408
|
+
written++;
|
|
409
|
+
}
|
|
410
|
+
const manifest = {
|
|
411
|
+
backupId,
|
|
412
|
+
createdAt: now.toISOString(),
|
|
413
|
+
instructionCount: written,
|
|
414
|
+
schemaVersion: bundle.manifest?.schemaVersion || 'imported',
|
|
415
|
+
source: 'file-import',
|
|
416
|
+
};
|
|
417
|
+
zip.addFile('manifest.json', Buffer.from(JSON.stringify(manifest, null, 2)));
|
|
418
|
+
zip.writeZip(zipPath);
|
|
419
|
+
process.stderr.write(`[admin] Imported backup from file: ${backupId}.zip (${written} files)\n`);
|
|
420
|
+
return { success: true, message: `Imported ${written} files as ${backupId}`, backupId, files: written };
|
|
421
|
+
}
|
|
422
|
+
catch (error) {
|
|
423
|
+
return { success: false, message: `Import failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Calculate current CPU usage with historical tracking
|
|
428
|
+
*/
|
|
429
|
+
calculateCpuUsage() {
|
|
430
|
+
const currentCpuUsage = process.cpuUsage();
|
|
431
|
+
let cpuPercent = 0;
|
|
432
|
+
let userTime = 0;
|
|
433
|
+
let systemTime = 0;
|
|
434
|
+
if (this.lastCpuUsage) {
|
|
435
|
+
// Calculate delta since last measurement
|
|
436
|
+
const userDelta = currentCpuUsage.user - this.lastCpuUsage.user;
|
|
437
|
+
const systemDelta = currentCpuUsage.system - this.lastCpuUsage.system;
|
|
438
|
+
const totalDelta = userDelta + systemDelta;
|
|
439
|
+
// Convert microseconds to percentage (assuming 1 second interval)
|
|
440
|
+
// For more accurate results, you'd want to track the actual time interval
|
|
441
|
+
cpuPercent = Math.min((totalDelta / 1000000) * 100, 100);
|
|
442
|
+
userTime = userDelta / 1000000; // Convert to seconds
|
|
443
|
+
systemTime = systemDelta / 1000000; // Convert to seconds
|
|
444
|
+
}
|
|
445
|
+
this.lastCpuUsage = currentCpuUsage;
|
|
446
|
+
// Add to history for leak detection
|
|
447
|
+
const historyEntry = {
|
|
448
|
+
timestamp: Date.now(),
|
|
449
|
+
user: userTime,
|
|
450
|
+
system: systemTime,
|
|
451
|
+
percent: cpuPercent
|
|
452
|
+
};
|
|
453
|
+
this.cpuHistory.push(historyEntry);
|
|
454
|
+
// Keep only recent entries
|
|
455
|
+
if (this.cpuHistory.length > this.maxCpuHistoryEntries) {
|
|
456
|
+
this.cpuHistory.shift();
|
|
457
|
+
}
|
|
458
|
+
return {
|
|
459
|
+
user: userTime,
|
|
460
|
+
system: systemTime,
|
|
461
|
+
percent: cpuPercent
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Analyze CPU trends for potential leaks
|
|
466
|
+
*/
|
|
467
|
+
analyzeCpuTrends() {
|
|
468
|
+
if (this.cpuHistory.length < 10) {
|
|
469
|
+
return { trend: 'stable', avgUsage: 0, peakUsage: 0 };
|
|
470
|
+
}
|
|
471
|
+
const recent = this.cpuHistory.slice(-10);
|
|
472
|
+
const avgUsage = recent.reduce((sum, entry) => sum + entry.percent, 0) / recent.length;
|
|
473
|
+
const peakUsage = Math.max(...recent.map(entry => entry.percent));
|
|
474
|
+
// Simple trend analysis - compare first half vs second half
|
|
475
|
+
const firstHalf = recent.slice(0, 5);
|
|
476
|
+
const secondHalf = recent.slice(5);
|
|
477
|
+
const firstAvg = firstHalf.reduce((sum, entry) => sum + entry.percent, 0) / firstHalf.length;
|
|
478
|
+
const secondAvg = secondHalf.reduce((sum, entry) => sum + entry.percent, 0) / secondHalf.length;
|
|
479
|
+
let trend = 'stable';
|
|
480
|
+
const difference = secondAvg - firstAvg;
|
|
481
|
+
if (Math.abs(difference) > 5) {
|
|
482
|
+
trend = difference > 0 ? 'increasing' : 'decreasing';
|
|
483
|
+
}
|
|
484
|
+
return { trend, avgUsage, peakUsage };
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Analyze memory usage trends for leak detection
|
|
488
|
+
*/
|
|
489
|
+
analyzeMemoryTrends() {
|
|
490
|
+
if (this.memoryHistory.length < 10) {
|
|
491
|
+
return { trend: 'stable', avgHeapUsed: 0, peakHeapUsed: 0, growthRate: 0 };
|
|
492
|
+
}
|
|
493
|
+
const recent = this.memoryHistory.slice(-10);
|
|
494
|
+
const avgHeapUsed = recent.reduce((sum, entry) => sum + entry.heapUsed, 0) / recent.length;
|
|
495
|
+
const peakHeapUsed = Math.max(...recent.map(entry => entry.heapUsed));
|
|
496
|
+
// Calculate growth rate (bytes per minute)
|
|
497
|
+
const firstEntry = recent[0];
|
|
498
|
+
const lastEntry = recent[recent.length - 1];
|
|
499
|
+
const timeDiffMinutes = (lastEntry.timestamp - firstEntry.timestamp) / (1000 * 60);
|
|
500
|
+
const growthRate = timeDiffMinutes > 0 ? (lastEntry.heapUsed - firstEntry.heapUsed) / timeDiffMinutes : 0;
|
|
501
|
+
// Simple trend analysis - compare first half vs second half
|
|
502
|
+
const firstHalf = recent.slice(0, 5);
|
|
503
|
+
const secondHalf = recent.slice(5);
|
|
504
|
+
const firstAvg = firstHalf.reduce((sum, entry) => sum + entry.heapUsed, 0) / firstHalf.length;
|
|
505
|
+
const secondAvg = secondHalf.reduce((sum, entry) => sum + entry.heapUsed, 0) / secondHalf.length;
|
|
506
|
+
let trend = 'stable';
|
|
507
|
+
const difference = secondAvg - firstAvg;
|
|
508
|
+
// Consider memory leak if growth is > 10MB or growth rate > 1MB/min
|
|
509
|
+
if (Math.abs(difference) > 10 * 1024 * 1024 || Math.abs(growthRate) > 1024 * 1024) {
|
|
510
|
+
trend = difference > 0 ? 'increasing' : 'decreasing';
|
|
511
|
+
}
|
|
512
|
+
return { trend, avgHeapUsed, peakHeapUsed, growthRate };
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Get comprehensive admin statistics
|
|
516
|
+
*/
|
|
517
|
+
getAdminStats() {
|
|
518
|
+
// Use real metrics snapshot (deterministic values)
|
|
519
|
+
const collector = (0, MetricsCollector_1.getMetricsCollector)();
|
|
520
|
+
const snapshot = collector.getCurrentSnapshot();
|
|
521
|
+
// Aggregate total requests from tool metrics
|
|
522
|
+
let totalRequests = 0;
|
|
523
|
+
Object.values(snapshot.tools).forEach(t => { totalRequests += t.callCount; });
|
|
524
|
+
// Get index validation summary for accurate accepted vs raw counts
|
|
525
|
+
let accepted = 0, scanned = 0, skipped = 0;
|
|
526
|
+
try {
|
|
527
|
+
const st = (0, indexContext_1.getIndexState)();
|
|
528
|
+
if (st.loadSummary) {
|
|
529
|
+
accepted = st.loadSummary.accepted;
|
|
530
|
+
scanned = st.loadSummary.scanned;
|
|
531
|
+
skipped = st.loadSummary.skipped;
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
accepted = st.list.length;
|
|
535
|
+
scanned = st.loadDebug?.scanned ?? accepted;
|
|
536
|
+
skipped = Math.max(0, scanned - accepted);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
catch {
|
|
540
|
+
/* ignore */
|
|
541
|
+
}
|
|
542
|
+
// Count physical *.json files (raw) deterministically from FS (may equal scanned; retained for transparency)
|
|
543
|
+
const indexDir = this.instructionsRoot;
|
|
544
|
+
let rawFileCount = scanned; // default to scanned
|
|
545
|
+
try {
|
|
546
|
+
if (fs_1.default.existsSync(indexDir)) {
|
|
547
|
+
rawFileCount = fs_1.default.readdirSync(indexDir).filter(f => f.toLowerCase().endsWith('.json')).length;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
catch { /* ignore */ }
|
|
551
|
+
// Recompute schema version snapshot only when any of these counts change
|
|
552
|
+
const cacheNeedsUpdate = !this.indexStatsCache || this.indexStatsCache.acceptedInstructions !== accepted || this.indexStatsCache.rawFileCount !== rawFileCount || this.indexStatsCache.skippedInstructions !== skipped;
|
|
553
|
+
if (cacheNeedsUpdate) {
|
|
554
|
+
const schemaVersions = new Set();
|
|
555
|
+
try {
|
|
556
|
+
if (fs_1.default.existsSync(indexDir)) {
|
|
557
|
+
const files = fs_1.default.readdirSync(indexDir).filter(f => f.toLowerCase().endsWith('.json')).slice(0, 200); // cap scan
|
|
558
|
+
for (const f of files) {
|
|
559
|
+
try {
|
|
560
|
+
const raw = fs_1.default.readFileSync(path_1.default.join(indexDir, f), 'utf-8');
|
|
561
|
+
const json = JSON.parse(raw);
|
|
562
|
+
if (typeof json.schemaVersion === 'string')
|
|
563
|
+
schemaVersions.add(json.schemaVersion);
|
|
564
|
+
}
|
|
565
|
+
catch { /* ignore parse */ }
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
catch { /* ignore */ }
|
|
570
|
+
const schemaVersion = schemaVersions.size === 0 ? 'unknown' : (schemaVersions.size === 1 ? Array.from(schemaVersions)[0] : `mixed(${Array.from(schemaVersions).join(',')})`);
|
|
571
|
+
this.indexStatsCache = {
|
|
572
|
+
totalInstructions: accepted, // maintain backward compatibility; semantic now accepted
|
|
573
|
+
acceptedInstructions: accepted,
|
|
574
|
+
rawFileCount,
|
|
575
|
+
skippedInstructions: skipped,
|
|
576
|
+
lastUpdated: new Date(),
|
|
577
|
+
version: snapshot.server.version,
|
|
578
|
+
schemaVersion
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
const memUsage = snapshot.server.memoryUsage; // already captured in snapshot
|
|
582
|
+
const cpuUsage = this.calculateCpuUsage(); // Calculate current CPU usage
|
|
583
|
+
// Ensure cache populated (should be by logic above, but safeguard for strict types)
|
|
584
|
+
if (!this.indexStatsCache) {
|
|
585
|
+
this.indexStatsCache = {
|
|
586
|
+
totalInstructions: accepted,
|
|
587
|
+
acceptedInstructions: accepted,
|
|
588
|
+
rawFileCount,
|
|
589
|
+
skippedInstructions: skipped,
|
|
590
|
+
lastUpdated: new Date(),
|
|
591
|
+
version: snapshot.server.version,
|
|
592
|
+
schemaVersion: 'unknown'
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
return {
|
|
596
|
+
// Total historical websocket connections (connected + disconnected)
|
|
597
|
+
totalConnections: snapshot.connections.totalConnections,
|
|
598
|
+
// Active websocket connections (live WS clients). Previously this returned only admin sessions size,
|
|
599
|
+
// which caused the UI to show 0 even when multiple WS clients were connected. This now reflects
|
|
600
|
+
// real-time active websocket connections from metrics.
|
|
601
|
+
activeConnections: snapshot.connections.activeConnections,
|
|
602
|
+
// Preserve visibility into admin (logical) sessions separately.
|
|
603
|
+
adminActiveSessions: this.panelState.activeSessions.size,
|
|
604
|
+
totalRequests,
|
|
605
|
+
errorRate: snapshot.performance.errorRate,
|
|
606
|
+
avgResponseTime: snapshot.performance.avgResponseTime,
|
|
607
|
+
uptime: Math.floor(snapshot.server.uptime / 1000), // seconds
|
|
608
|
+
memoryUsage: {
|
|
609
|
+
heapUsed: memUsage.heapUsed,
|
|
610
|
+
heapTotal: memUsage.heapTotal,
|
|
611
|
+
external: memUsage?.external ?? 0,
|
|
612
|
+
...(memUsage?.heapLimit ? { heapLimit: memUsage.heapLimit } : {})
|
|
613
|
+
},
|
|
614
|
+
cpuUsage,
|
|
615
|
+
toolMetrics: snapshot.tools,
|
|
616
|
+
indexStats: this.indexStatsCache
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
updateSystemHealth() {
|
|
620
|
+
const issues = [];
|
|
621
|
+
const recommendations = [];
|
|
622
|
+
// Check memory usage and track history
|
|
623
|
+
const memUsage = process.memoryUsage();
|
|
624
|
+
// Use V8 heap_size_limit for percentage -- heapTotal tracks closely
|
|
625
|
+
// behind heapUsed and would false-alarm at low absolute usage.
|
|
626
|
+
const v8Stats = v8_1.default.getHeapStatistics();
|
|
627
|
+
const heapLimit = v8Stats.heap_size_limit || memUsage.heapTotal;
|
|
628
|
+
const memPercent = (memUsage.heapUsed / heapLimit) * 100;
|
|
629
|
+
// Track memory history for leak detection
|
|
630
|
+
this.memoryHistory.push({
|
|
631
|
+
timestamp: Date.now(),
|
|
632
|
+
heapUsed: memUsage.heapUsed,
|
|
633
|
+
heapTotal: memUsage.heapTotal,
|
|
634
|
+
external: memUsage.external,
|
|
635
|
+
rss: memUsage.rss
|
|
636
|
+
});
|
|
637
|
+
// Maintain memory history buffer
|
|
638
|
+
if (this.memoryHistory.length > this.maxMemoryHistoryEntries) {
|
|
639
|
+
this.memoryHistory.shift();
|
|
640
|
+
}
|
|
641
|
+
if (memPercent > 80) {
|
|
642
|
+
issues.push('High memory usage detected');
|
|
643
|
+
recommendations.push('Consider restarting the server or increasing memory limits');
|
|
644
|
+
}
|
|
645
|
+
// Check memory trends for leak detection
|
|
646
|
+
const memoryTrends = this.analyzeMemoryTrends();
|
|
647
|
+
if (memoryTrends.trend === 'increasing' && memoryTrends.growthRate > 1024 * 1024) {
|
|
648
|
+
issues.push('Memory leak detected - heap growing consistently');
|
|
649
|
+
recommendations.push('Investigate memory usage patterns and potential leaks');
|
|
650
|
+
}
|
|
651
|
+
if (memoryTrends.growthRate > 5 * 1024 * 1024) { // > 5MB/min growth
|
|
652
|
+
issues.push('Rapid memory growth detected');
|
|
653
|
+
recommendations.push('Monitor memory usage closely and consider restart if growth continues');
|
|
654
|
+
}
|
|
655
|
+
// Check CPU usage and trends
|
|
656
|
+
const cpuTrends = this.analyzeCpuTrends();
|
|
657
|
+
if (cpuTrends.avgUsage > 80) {
|
|
658
|
+
issues.push('High CPU usage detected');
|
|
659
|
+
recommendations.push('Review server load and consider scaling');
|
|
660
|
+
}
|
|
661
|
+
if (cpuTrends.trend === 'increasing' && cpuTrends.avgUsage > 50) {
|
|
662
|
+
issues.push('CPU usage trend increasing');
|
|
663
|
+
recommendations.push('Monitor for potential CPU leaks or resource contention');
|
|
664
|
+
}
|
|
665
|
+
if (cpuTrends.peakUsage > 95) {
|
|
666
|
+
issues.push('CPU usage spikes detected');
|
|
667
|
+
recommendations.push('Investigate CPU-intensive operations');
|
|
668
|
+
}
|
|
669
|
+
// Check uptime (regression & long-running)
|
|
670
|
+
const currentUptimeSeconds = Math.floor(process.uptime());
|
|
671
|
+
const uptimeHours = currentUptimeSeconds / 3600;
|
|
672
|
+
if (this.lastUptimeSeconds > 0 && currentUptimeSeconds < this.lastUptimeSeconds) {
|
|
673
|
+
// Uptime decreased => restart/regression
|
|
674
|
+
issues.push('Uptime regression detected (server restart)');
|
|
675
|
+
recommendations.push('Review restart reason and ensure intentional');
|
|
676
|
+
}
|
|
677
|
+
else if (uptimeHours > 72) {
|
|
678
|
+
recommendations.push('Consider scheduled restart for optimal performance');
|
|
679
|
+
}
|
|
680
|
+
this.lastUptimeSeconds = currentUptimeSeconds;
|
|
681
|
+
// Check error rate
|
|
682
|
+
const errorRate = this.getErrorRate();
|
|
683
|
+
if (errorRate > 5) {
|
|
684
|
+
issues.push('Elevated error rate detected');
|
|
685
|
+
recommendations.push('Review error logs and investigate root causes');
|
|
686
|
+
}
|
|
687
|
+
// Determine overall health status
|
|
688
|
+
let status = 'healthy';
|
|
689
|
+
if (issues.length > 0) {
|
|
690
|
+
status = (memPercent > 90 || errorRate > 10 || cpuTrends.avgUsage > 90 || memoryTrends.growthRate > 10 * 1024 * 1024) ? 'critical' : 'warning';
|
|
691
|
+
}
|
|
692
|
+
this.maintenanceInfo.systemHealth = {
|
|
693
|
+
status,
|
|
694
|
+
issues,
|
|
695
|
+
recommendations,
|
|
696
|
+
cpuTrend: cpuTrends.trend,
|
|
697
|
+
memoryTrend: memoryTrends.trend,
|
|
698
|
+
memoryGrowthRate: memoryTrends.growthRate
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
/** Return immutable copy of session history */
|
|
702
|
+
getSessionHistory(limit) {
|
|
703
|
+
return this.panelState.getSessionHistory(limit);
|
|
704
|
+
}
|
|
705
|
+
async clearSessionHistory() {
|
|
706
|
+
return this.panelState.clearSessionHistory();
|
|
707
|
+
}
|
|
708
|
+
updateSessionActivity(sessionId) {
|
|
709
|
+
return this.panelState.updateSessionActivity(sessionId);
|
|
710
|
+
}
|
|
711
|
+
getTotalConnections() {
|
|
712
|
+
return (0, MetricsCollector_1.getMetricsCollector)().getCurrentSnapshot().connections.totalConnections;
|
|
713
|
+
}
|
|
714
|
+
getTotalRequests() {
|
|
715
|
+
const snap = (0, MetricsCollector_1.getMetricsCollector)().getCurrentSnapshot();
|
|
716
|
+
return Object.values(snap.tools).reduce((sum, t) => sum + t.callCount, 0);
|
|
717
|
+
}
|
|
718
|
+
getErrorRate() {
|
|
719
|
+
return (0, MetricsCollector_1.getMetricsCollector)().getCurrentSnapshot().performance.errorRate;
|
|
720
|
+
}
|
|
721
|
+
getAvgResponseTime() {
|
|
722
|
+
return (0, MetricsCollector_1.getMetricsCollector)().getCurrentSnapshot().performance.avgResponseTime;
|
|
723
|
+
}
|
|
724
|
+
getIndexInstructionCount() {
|
|
725
|
+
const indexDir = this.instructionsRoot;
|
|
726
|
+
try {
|
|
727
|
+
if (fs_1.default.existsSync(indexDir)) {
|
|
728
|
+
return fs_1.default.readdirSync(indexDir).filter(f => f.toLowerCase().endsWith('.json')).length;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
catch {
|
|
732
|
+
// ignore
|
|
733
|
+
}
|
|
734
|
+
return 0;
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Restart server components
|
|
738
|
+
*/
|
|
739
|
+
async restartServer(component = 'all') {
|
|
740
|
+
try {
|
|
741
|
+
process.stderr.write(`[admin] Restart requested for component: ${component}\n`);
|
|
742
|
+
// In real implementation, this would perform actual component restarts
|
|
743
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
744
|
+
return {
|
|
745
|
+
success: true,
|
|
746
|
+
message: `${component} restart completed successfully`
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
catch (error) {
|
|
750
|
+
return {
|
|
751
|
+
success: false,
|
|
752
|
+
message: `Restart failed: ${error instanceof Error ? error.message : String(error)}`
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Clear server caches
|
|
758
|
+
*/
|
|
759
|
+
clearCaches() {
|
|
760
|
+
try {
|
|
761
|
+
const cleared = [];
|
|
762
|
+
// Clear instruction cache
|
|
763
|
+
cleared.push('instruction_cache');
|
|
764
|
+
// Clear metrics cache
|
|
765
|
+
cleared.push('metrics_cache');
|
|
766
|
+
// Clear response cache
|
|
767
|
+
cleared.push('response_cache');
|
|
768
|
+
process.stderr.write(`[admin] Caches cleared: ${cleared.join(', ')}\n`);
|
|
769
|
+
return {
|
|
770
|
+
success: true,
|
|
771
|
+
message: 'All caches cleared successfully',
|
|
772
|
+
cleared
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
catch (error) {
|
|
776
|
+
return {
|
|
777
|
+
success: false,
|
|
778
|
+
message: `Failed to clear caches: ${error instanceof Error ? error.message : String(error)}`,
|
|
779
|
+
cleared: []
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
exports.AdminPanel = AdminPanel;
|
|
785
|
+
// Singleton instance
|
|
786
|
+
let adminPanelInstance = null;
|
|
787
|
+
function getAdminPanel() {
|
|
788
|
+
if (!adminPanelInstance) {
|
|
789
|
+
adminPanelInstance = new AdminPanel();
|
|
790
|
+
}
|
|
791
|
+
return adminPanelInstance;
|
|
792
|
+
}
|