@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.
Files changed (360) hide show
  1. package/CHANGELOG.md +1218 -0
  2. package/CODE_OF_CONDUCT.md +49 -0
  3. package/CONTRIBUTING.md +75 -0
  4. package/LICENSE +21 -0
  5. package/README.md +523 -0
  6. package/SECURITY.md +50 -0
  7. package/dist/config/configUtils.d.ts +11 -0
  8. package/dist/config/configUtils.js +87 -0
  9. package/dist/config/dashboardConfig.d.ts +45 -0
  10. package/dist/config/dashboardConfig.js +63 -0
  11. package/dist/config/defaultValues.d.ts +61 -0
  12. package/dist/config/defaultValues.js +70 -0
  13. package/dist/config/dirConstants.d.ts +17 -0
  14. package/dist/config/dirConstants.js +28 -0
  15. package/dist/config/featureConfig.d.ts +61 -0
  16. package/dist/config/featureConfig.js +121 -0
  17. package/dist/config/runtimeConfig.d.ts +145 -0
  18. package/dist/config/runtimeConfig.js +334 -0
  19. package/dist/config/serverConfig.d.ts +90 -0
  20. package/dist/config/serverConfig.js +164 -0
  21. package/dist/dashboard/analytics/AnalyticsEngine.d.ts +142 -0
  22. package/dist/dashboard/analytics/AnalyticsEngine.js +373 -0
  23. package/dist/dashboard/analytics/BusinessIntelligence.d.ts +187 -0
  24. package/dist/dashboard/analytics/BusinessIntelligence.js +594 -0
  25. package/dist/dashboard/client/admin.html +2150 -0
  26. package/dist/dashboard/client/chunks/mermaid-layout-elk.esm.min/chunk-SP2CHFBE.mjs +1 -0
  27. package/dist/dashboard/client/chunks/mermaid-layout-elk.esm.min/render-T6MDALS3.mjs +27 -0
  28. package/dist/dashboard/client/css/admin.css +1466 -0
  29. package/dist/dashboard/client/js/admin.boot.js +359 -0
  30. package/dist/dashboard/client/js/admin.config.js +196 -0
  31. package/dist/dashboard/client/js/admin.embeddings.js +425 -0
  32. package/dist/dashboard/client/js/admin.graph.js +583 -0
  33. package/dist/dashboard/client/js/admin.instances.js +120 -0
  34. package/dist/dashboard/client/js/admin.instructions.js +552 -0
  35. package/dist/dashboard/client/js/admin.logs.js +113 -0
  36. package/dist/dashboard/client/js/admin.maintenance.js +354 -0
  37. package/dist/dashboard/client/js/admin.messaging.js +635 -0
  38. package/dist/dashboard/client/js/admin.monitor.js +181 -0
  39. package/dist/dashboard/client/js/admin.overview.js +221 -0
  40. package/dist/dashboard/client/js/admin.performance.js +61 -0
  41. package/dist/dashboard/client/js/admin.sessions.js +293 -0
  42. package/dist/dashboard/client/js/admin.sqlite.js +366 -0
  43. package/dist/dashboard/client/js/admin.utils.js +49 -0
  44. package/dist/dashboard/client/js/chart.umd.js +14 -0
  45. package/dist/dashboard/client/js/elk.bundled.js +6696 -0
  46. package/dist/dashboard/client/js/marked.umd.js +74 -0
  47. package/dist/dashboard/client/js/mermaid.min.js +3022 -0
  48. package/dist/dashboard/client/mermaid-layout-elk.esm.min.mjs +1 -0
  49. package/dist/dashboard/export/DataExporter.d.ts +169 -0
  50. package/dist/dashboard/export/DataExporter.js +737 -0
  51. package/dist/dashboard/export/exporters/csvExporter.d.ts +11 -0
  52. package/dist/dashboard/export/exporters/csvExporter.js +46 -0
  53. package/dist/dashboard/export/exporters/exportTypes.d.ts +89 -0
  54. package/dist/dashboard/export/exporters/exportTypes.js +5 -0
  55. package/dist/dashboard/export/exporters/jsonExporter.d.ts +7 -0
  56. package/dist/dashboard/export/exporters/jsonExporter.js +22 -0
  57. package/dist/dashboard/export/exporters/xmlExporter.d.ts +17 -0
  58. package/dist/dashboard/export/exporters/xmlExporter.js +175 -0
  59. package/dist/dashboard/integration/APIIntegration.d.ts +41 -0
  60. package/dist/dashboard/integration/APIIntegration.js +95 -0
  61. package/dist/dashboard/security/SecurityMonitor.d.ts +167 -0
  62. package/dist/dashboard/security/SecurityMonitor.js +559 -0
  63. package/dist/dashboard/server/AdminPanel.d.ts +183 -0
  64. package/dist/dashboard/server/AdminPanel.js +792 -0
  65. package/dist/dashboard/server/AdminPanelConfig.d.ts +42 -0
  66. package/dist/dashboard/server/AdminPanelConfig.js +80 -0
  67. package/dist/dashboard/server/AdminPanelState.d.ts +47 -0
  68. package/dist/dashboard/server/AdminPanelState.js +214 -0
  69. package/dist/dashboard/server/ApiRoutes.d.ts +17 -0
  70. package/dist/dashboard/server/ApiRoutes.js +149 -0
  71. package/dist/dashboard/server/DashboardServer.d.ts +49 -0
  72. package/dist/dashboard/server/DashboardServer.js +159 -0
  73. package/dist/dashboard/server/FileMetricsStorage.d.ts +49 -0
  74. package/dist/dashboard/server/FileMetricsStorage.js +195 -0
  75. package/dist/dashboard/server/HttpTransport.d.ts +23 -0
  76. package/dist/dashboard/server/HttpTransport.js +116 -0
  77. package/dist/dashboard/server/InstanceManager.d.ts +53 -0
  78. package/dist/dashboard/server/InstanceManager.js +284 -0
  79. package/dist/dashboard/server/KnowledgeStore.d.ts +35 -0
  80. package/dist/dashboard/server/KnowledgeStore.js +105 -0
  81. package/dist/dashboard/server/LeaderElection.d.ts +81 -0
  82. package/dist/dashboard/server/LeaderElection.js +268 -0
  83. package/dist/dashboard/server/MetricsCollector.d.ts +200 -0
  84. package/dist/dashboard/server/MetricsCollector.js +803 -0
  85. package/dist/dashboard/server/SessionPersistenceManager.d.ts +88 -0
  86. package/dist/dashboard/server/SessionPersistenceManager.js +457 -0
  87. package/dist/dashboard/server/ThinClient.d.ts +64 -0
  88. package/dist/dashboard/server/ThinClient.js +237 -0
  89. package/dist/dashboard/server/WebSocketManager.d.ts +161 -0
  90. package/dist/dashboard/server/WebSocketManager.js +463 -0
  91. package/dist/dashboard/server/httpLifecycle.d.ts +17 -0
  92. package/dist/dashboard/server/httpLifecycle.js +35 -0
  93. package/dist/dashboard/server/legacyDashboardHtml.d.ts +9 -0
  94. package/dist/dashboard/server/legacyDashboardHtml.js +618 -0
  95. package/dist/dashboard/server/legacyDashboardStyles.d.ts +5 -0
  96. package/dist/dashboard/server/legacyDashboardStyles.js +490 -0
  97. package/dist/dashboard/server/metricsAggregation.d.ts +252 -0
  98. package/dist/dashboard/server/metricsAggregation.js +206 -0
  99. package/dist/dashboard/server/metricsSerializer.d.ts +25 -0
  100. package/dist/dashboard/server/metricsSerializer.js +195 -0
  101. package/dist/dashboard/server/routes/admin.routes.d.ts +16 -0
  102. package/dist/dashboard/server/routes/admin.routes.js +596 -0
  103. package/dist/dashboard/server/routes/alerts.routes.d.ts +7 -0
  104. package/dist/dashboard/server/routes/alerts.routes.js +93 -0
  105. package/dist/dashboard/server/routes/api.feedback.routes.d.ts +73 -0
  106. package/dist/dashboard/server/routes/api.feedback.routes.js +171 -0
  107. package/dist/dashboard/server/routes/api.instructions.routes.d.ts +101 -0
  108. package/dist/dashboard/server/routes/api.instructions.routes.js +213 -0
  109. package/dist/dashboard/server/routes/api.usage.routes.d.ts +57 -0
  110. package/dist/dashboard/server/routes/api.usage.routes.js +374 -0
  111. package/dist/dashboard/server/routes/embeddings.routes.d.ts +6 -0
  112. package/dist/dashboard/server/routes/embeddings.routes.js +246 -0
  113. package/dist/dashboard/server/routes/graph.routes.d.ts +6 -0
  114. package/dist/dashboard/server/routes/graph.routes.js +280 -0
  115. package/dist/dashboard/server/routes/index.d.ts +38 -0
  116. package/dist/dashboard/server/routes/index.js +194 -0
  117. package/dist/dashboard/server/routes/instances.routes.d.ts +6 -0
  118. package/dist/dashboard/server/routes/instances.routes.js +35 -0
  119. package/dist/dashboard/server/routes/instructions.routes.d.ts +8 -0
  120. package/dist/dashboard/server/routes/instructions.routes.js +336 -0
  121. package/dist/dashboard/server/routes/knowledge.routes.d.ts +6 -0
  122. package/dist/dashboard/server/routes/knowledge.routes.js +82 -0
  123. package/dist/dashboard/server/routes/logs.routes.d.ts +6 -0
  124. package/dist/dashboard/server/routes/logs.routes.js +164 -0
  125. package/dist/dashboard/server/routes/messaging.routes.d.ts +16 -0
  126. package/dist/dashboard/server/routes/messaging.routes.js +293 -0
  127. package/dist/dashboard/server/routes/metrics.routes.d.ts +10 -0
  128. package/dist/dashboard/server/routes/metrics.routes.js +346 -0
  129. package/dist/dashboard/server/routes/scripts.routes.d.ts +9 -0
  130. package/dist/dashboard/server/routes/scripts.routes.js +84 -0
  131. package/dist/dashboard/server/routes/sqlite.routes.d.ts +9 -0
  132. package/dist/dashboard/server/routes/sqlite.routes.js +569 -0
  133. package/dist/dashboard/server/routes/status.routes.d.ts +7 -0
  134. package/dist/dashboard/server/routes/status.routes.js +183 -0
  135. package/dist/dashboard/server/routes/synthetic.routes.d.ts +7 -0
  136. package/dist/dashboard/server/routes/synthetic.routes.js +195 -0
  137. package/dist/dashboard/server/routes/tools.routes.d.ts +6 -0
  138. package/dist/dashboard/server/routes/tools.routes.js +46 -0
  139. package/dist/dashboard/server/routes/usage.routes.d.ts +6 -0
  140. package/dist/dashboard/server/routes/usage.routes.js +25 -0
  141. package/dist/dashboard/server/wsInit.d.ts +16 -0
  142. package/dist/dashboard/server/wsInit.js +35 -0
  143. package/dist/externalClientLib.d.ts +1 -0
  144. package/dist/externalClientLib.js +2 -0
  145. package/dist/minimal/index.d.ts +1 -0
  146. package/dist/minimal/index.js +140 -0
  147. package/dist/models/SessionPersistence.d.ts +115 -0
  148. package/dist/models/SessionPersistence.js +66 -0
  149. package/dist/models/instruction.d.ts +45 -0
  150. package/dist/models/instruction.js +2 -0
  151. package/dist/perf/benchmark.d.ts +1 -0
  152. package/dist/perf/benchmark.js +50 -0
  153. package/dist/portableClientWrapper.d.ts +1 -0
  154. package/dist/portableClientWrapper.js +2 -0
  155. package/dist/schemas/index.d.ts +128 -0
  156. package/dist/schemas/index.js +371 -0
  157. package/dist/scripts/runPerformanceBaseline.d.ts +1 -0
  158. package/dist/scripts/runPerformanceBaseline.js +17 -0
  159. package/dist/server/handshakeManager.d.ts +25 -0
  160. package/dist/server/handshakeManager.js +472 -0
  161. package/dist/server/index-server.d.ts +56 -0
  162. package/dist/server/index-server.js +822 -0
  163. package/dist/server/registry.d.ts +44 -0
  164. package/dist/server/registry.js +236 -0
  165. package/dist/server/sdkServer.d.ts +8 -0
  166. package/dist/server/sdkServer.js +299 -0
  167. package/dist/server/shutdownGuard.d.ts +41 -0
  168. package/dist/server/shutdownGuard.js +52 -0
  169. package/dist/server/thin-client.d.ts +22 -0
  170. package/dist/server/thin-client.js +111 -0
  171. package/dist/server/transport.d.ts +41 -0
  172. package/dist/server/transport.js +312 -0
  173. package/dist/server/transportFactory.d.ts +21 -0
  174. package/dist/server/transportFactory.js +429 -0
  175. package/dist/services/atomicFs.d.ts +22 -0
  176. package/dist/services/atomicFs.js +103 -0
  177. package/dist/services/auditLog.d.ts +38 -0
  178. package/dist/services/auditLog.js +142 -0
  179. package/dist/services/autoBackup.d.ts +14 -0
  180. package/dist/services/autoBackup.js +171 -0
  181. package/dist/services/autoSplit.d.ts +32 -0
  182. package/dist/services/autoSplit.js +113 -0
  183. package/dist/services/backupZip.d.ts +25 -0
  184. package/dist/services/backupZip.js +110 -0
  185. package/dist/services/bootstrapGating.d.ts +123 -0
  186. package/dist/services/bootstrapGating.js +221 -0
  187. package/dist/services/canonical.d.ts +23 -0
  188. package/dist/services/canonical.js +65 -0
  189. package/dist/services/categoryRules.d.ts +7 -0
  190. package/dist/services/categoryRules.js +37 -0
  191. package/dist/services/classificationService.d.ts +42 -0
  192. package/dist/services/classificationService.js +168 -0
  193. package/dist/services/embeddingService.d.ts +62 -0
  194. package/dist/services/embeddingService.js +259 -0
  195. package/dist/services/errors.d.ts +22 -0
  196. package/dist/services/errors.js +31 -0
  197. package/dist/services/featureFlags.d.ts +25 -0
  198. package/dist/services/featureFlags.js +89 -0
  199. package/dist/services/features.d.ts +13 -0
  200. package/dist/services/features.js +35 -0
  201. package/dist/services/handlers/instructions.add.d.ts +1 -0
  202. package/dist/services/handlers/instructions.add.js +496 -0
  203. package/dist/services/handlers/instructions.groom.d.ts +1 -0
  204. package/dist/services/handlers/instructions.groom.js +523 -0
  205. package/dist/services/handlers/instructions.import.d.ts +1 -0
  206. package/dist/services/handlers/instructions.import.js +173 -0
  207. package/dist/services/handlers/instructions.patch.d.ts +1 -0
  208. package/dist/services/handlers/instructions.patch.js +167 -0
  209. package/dist/services/handlers/instructions.query.d.ts +163 -0
  210. package/dist/services/handlers/instructions.query.js +522 -0
  211. package/dist/services/handlers/instructions.reload.d.ts +1 -0
  212. package/dist/services/handlers/instructions.reload.js +13 -0
  213. package/dist/services/handlers/instructions.remove.d.ts +1 -0
  214. package/dist/services/handlers/instructions.remove.js +118 -0
  215. package/dist/services/handlers/instructions.shared.d.ts +31 -0
  216. package/dist/services/handlers/instructions.shared.js +124 -0
  217. package/dist/services/handlers.activation.d.ts +1 -0
  218. package/dist/services/handlers.activation.js +203 -0
  219. package/dist/services/handlers.bootstrap.d.ts +1 -0
  220. package/dist/services/handlers.bootstrap.js +38 -0
  221. package/dist/services/handlers.dashboardConfig.d.ts +34 -0
  222. package/dist/services/handlers.dashboardConfig.js +108 -0
  223. package/dist/services/handlers.diagnostics.d.ts +1 -0
  224. package/dist/services/handlers.diagnostics.js +64 -0
  225. package/dist/services/handlers.feedback.d.ts +15 -0
  226. package/dist/services/handlers.feedback.js +378 -0
  227. package/dist/services/handlers.gates.d.ts +1 -0
  228. package/dist/services/handlers.gates.js +46 -0
  229. package/dist/services/handlers.graph.d.ts +53 -0
  230. package/dist/services/handlers.graph.js +231 -0
  231. package/dist/services/handlers.help.d.ts +1 -0
  232. package/dist/services/handlers.help.js +119 -0
  233. package/dist/services/handlers.instructionSchema.d.ts +1 -0
  234. package/dist/services/handlers.instructionSchema.js +227 -0
  235. package/dist/services/handlers.instructions.d.ts +8 -0
  236. package/dist/services/handlers.instructions.js +14 -0
  237. package/dist/services/handlers.instructionsDiagnostics.d.ts +1 -0
  238. package/dist/services/handlers.instructionsDiagnostics.js +14 -0
  239. package/dist/services/handlers.integrity.d.ts +1 -0
  240. package/dist/services/handlers.integrity.js +35 -0
  241. package/dist/services/handlers.manifest.d.ts +1 -0
  242. package/dist/services/handlers.manifest.js +24 -0
  243. package/dist/services/handlers.messaging.d.ts +12 -0
  244. package/dist/services/handlers.messaging.js +203 -0
  245. package/dist/services/handlers.metrics.d.ts +1 -0
  246. package/dist/services/handlers.metrics.js +43 -0
  247. package/dist/services/handlers.promote.d.ts +1 -0
  248. package/dist/services/handlers.promote.js +306 -0
  249. package/dist/services/handlers.prompt.d.ts +1 -0
  250. package/dist/services/handlers.prompt.js +7 -0
  251. package/dist/services/handlers.search.d.ts +69 -0
  252. package/dist/services/handlers.search.js +645 -0
  253. package/dist/services/handlers.testPrimitive.d.ts +1 -0
  254. package/dist/services/handlers.testPrimitive.js +5 -0
  255. package/dist/services/handlers.trace.d.ts +1 -0
  256. package/dist/services/handlers.trace.js +31 -0
  257. package/dist/services/handlers.usage.d.ts +1 -0
  258. package/dist/services/handlers.usage.js +11 -0
  259. package/dist/services/hotScore.d.ts +137 -0
  260. package/dist/services/hotScore.js +244 -0
  261. package/dist/services/indexContext.d.ts +117 -0
  262. package/dist/services/indexContext.js +968 -0
  263. package/dist/services/indexLoader.d.ts +44 -0
  264. package/dist/services/indexLoader.js +921 -0
  265. package/dist/services/indexRepository.d.ts +32 -0
  266. package/dist/services/indexRepository.js +71 -0
  267. package/dist/services/indexingService.d.ts +1 -0
  268. package/dist/services/indexingService.js +2 -0
  269. package/dist/services/instructions.dispatcher.d.ts +1 -0
  270. package/dist/services/instructions.dispatcher.js +231 -0
  271. package/dist/services/logPrefix.d.ts +1 -0
  272. package/dist/services/logPrefix.js +30 -0
  273. package/dist/services/logger.d.ts +52 -0
  274. package/dist/services/logger.js +268 -0
  275. package/dist/services/manifestManager.d.ts +82 -0
  276. package/dist/services/manifestManager.js +200 -0
  277. package/dist/services/messaging/agentMailbox.d.ts +60 -0
  278. package/dist/services/messaging/agentMailbox.js +353 -0
  279. package/dist/services/messaging/messagingPersistence.d.ts +20 -0
  280. package/dist/services/messaging/messagingPersistence.js +111 -0
  281. package/dist/services/messaging/messagingTypes.d.ts +150 -0
  282. package/dist/services/messaging/messagingTypes.js +66 -0
  283. package/dist/services/ownershipService.d.ts +1 -0
  284. package/dist/services/ownershipService.js +38 -0
  285. package/dist/services/performanceBaseline.d.ts +19 -0
  286. package/dist/services/performanceBaseline.js +210 -0
  287. package/dist/services/preflight.d.ts +12 -0
  288. package/dist/services/preflight.js +79 -0
  289. package/dist/services/promptReviewService.d.ts +44 -0
  290. package/dist/services/promptReviewService.js +101 -0
  291. package/dist/services/responseEnvelope.d.ts +6 -0
  292. package/dist/services/responseEnvelope.js +25 -0
  293. package/dist/services/seedBootstrap.d.ts +34 -0
  294. package/dist/services/seedBootstrap.js +427 -0
  295. package/dist/services/storage/factory.d.ts +17 -0
  296. package/dist/services/storage/factory.js +35 -0
  297. package/dist/services/storage/hashUtils.d.ts +11 -0
  298. package/dist/services/storage/hashUtils.js +35 -0
  299. package/dist/services/storage/index.d.ts +12 -0
  300. package/dist/services/storage/index.js +18 -0
  301. package/dist/services/storage/jsonFileStore.d.ts +32 -0
  302. package/dist/services/storage/jsonFileStore.js +241 -0
  303. package/dist/services/storage/migrationEngine.d.ts +35 -0
  304. package/dist/services/storage/migrationEngine.js +93 -0
  305. package/dist/services/storage/sqliteMessageStore.d.ts +53 -0
  306. package/dist/services/storage/sqliteMessageStore.js +146 -0
  307. package/dist/services/storage/sqliteSchema.d.ts +12 -0
  308. package/dist/services/storage/sqliteSchema.js +122 -0
  309. package/dist/services/storage/sqliteStore.d.ts +41 -0
  310. package/dist/services/storage/sqliteStore.js +339 -0
  311. package/dist/services/storage/sqliteUsageStore.d.ts +35 -0
  312. package/dist/services/storage/sqliteUsageStore.js +94 -0
  313. package/dist/services/storage/types.d.ts +171 -0
  314. package/dist/services/storage/types.js +12 -0
  315. package/dist/services/toolHandlers.d.ts +23 -0
  316. package/dist/services/toolHandlers.js +50 -0
  317. package/dist/services/toolRegistry.d.ts +20 -0
  318. package/dist/services/toolRegistry.js +490 -0
  319. package/dist/services/toolRegistry.zod.d.ts +10 -0
  320. package/dist/services/toolRegistry.zod.js +323 -0
  321. package/dist/services/tracing.d.ts +26 -0
  322. package/dist/services/tracing.js +260 -0
  323. package/dist/services/usageBuckets.d.ts +161 -0
  324. package/dist/services/usageBuckets.js +364 -0
  325. package/dist/services/validationService.d.ts +38 -0
  326. package/dist/services/validationService.js +125 -0
  327. package/dist/utils/BufferRing.d.ts +203 -0
  328. package/dist/utils/BufferRing.js +551 -0
  329. package/dist/utils/BufferRingExamples.d.ts +55 -0
  330. package/dist/utils/BufferRingExamples.js +188 -0
  331. package/dist/utils/envUtils.d.ts +42 -0
  332. package/dist/utils/envUtils.js +80 -0
  333. package/dist/utils/memoryMonitor.d.ts +83 -0
  334. package/dist/utils/memoryMonitor.js +275 -0
  335. package/dist/versioning/schemaVersion.d.ts +6 -0
  336. package/dist/versioning/schemaVersion.js +93 -0
  337. package/package.json +134 -0
  338. package/schemas/README.md +13 -0
  339. package/schemas/feedback-entry.schema.json +27 -0
  340. package/schemas/graph-export-v2.schema.json +60 -0
  341. package/schemas/index-server.code-schema.json +38477 -0
  342. package/schemas/instruction.schema.json +262 -0
  343. package/schemas/json-schema/SessionPersistence-persisted-admin-session.schema.json +54 -0
  344. package/schemas/json-schema/SessionPersistence-persisted-session-history-entry.schema.json +51 -0
  345. package/schemas/json-schema/SessionPersistence-persisted-web-socket-connection.schema.json +54 -0
  346. package/schemas/json-schema/SessionPersistence-session-persistence-config.schema.json +110 -0
  347. package/schemas/json-schema/SessionPersistence-session-persistence-data.schema.json +229 -0
  348. package/schemas/json-schema/SessionPersistence-session-persistence-manifest.schema.json +109 -0
  349. package/schemas/json-schema/SessionPersistence-session-persistence-metadata.schema.json +55 -0
  350. package/schemas/json-schema/instruction-audience-scope.schema.json +14 -0
  351. package/schemas/json-schema/instruction-content-type.schema.json +17 -0
  352. package/schemas/json-schema/instruction-instruction-entry.schema.json +206 -0
  353. package/schemas/json-schema/instruction-requirement-level.schema.json +16 -0
  354. package/schemas/manifest.json +78 -0
  355. package/schemas/manifest.schema.json +33 -0
  356. package/schemas/usage-batch.schema.json +16 -0
  357. package/schemas/usage-buckets.schema.json +30 -0
  358. package/schemas/usage-event.schema.json +17 -0
  359. package/scripts/copy-dashboard-assets.mjs +170 -0
  360. package/scripts/setup-hooks.cjs +28 -0
@@ -0,0 +1,359 @@
1
+ /* eslint-disable */
2
+ // admin.boot.js
3
+ (function(){
4
+ 'use strict';
5
+
6
+ // Minimal bootstrapping for the admin UI. Keep this file small so it can be loaded early.
7
+ function showSection(section){
8
+ document.querySelectorAll('.admin-section').forEach(s => s.classList.add('hidden'));
9
+ let activeSection = document.getElementById(section + '-section');
10
+ // Fallback resolution: allow passing full id (e.g. 'overview-section') or data-section name
11
+ if(!activeSection){
12
+ if(section.endsWith('-section')) activeSection = document.getElementById(section);
13
+ if(!activeSection){
14
+ // Try match by prefix (id starts with section)
15
+ activeSection = Array.from(document.querySelectorAll('.admin-section')).find(s=>s.id === section || s.id === section+'-section' || s.id.startsWith(section));
16
+ }
17
+ }
18
+ if (activeSection) {
19
+ activeSection.classList.remove('hidden');
20
+ } else {
21
+ // Lightweight debug log; keep from spamming
22
+ if(!(window.__lastMissingSection === section)) {
23
+ console.warn('[admin] section not found', section);
24
+ window.__lastMissingSection = section;
25
+ }
26
+ }
27
+ // Update nav buttons
28
+ document.querySelectorAll('.nav-btn').forEach(btn => {
29
+ const ds = btn.getAttribute('data-section');
30
+ if(ds === section) btn.classList.add('active'); else btn.classList.remove('active');
31
+ });
32
+ window.currentSection = section;
33
+ // Lazy-load graph module when needed (mermaid + elk heavy). This keeps initial boot small.
34
+ if(section === 'graph'){
35
+ ensureGraphModule().then(()=>{
36
+ if(typeof window.initGraphScopeDefaults === 'function') { try { window.initGraphScopeDefaults(); } catch(_){} }
37
+ // Deterministic ready chain replacing earlier ad-hoc reload
38
+ if(typeof window.graphEnsureReadyAndReload === 'function') {
39
+ try { window.graphEnsureReadyAndReload(); } catch{}
40
+ } else if(typeof window.reloadGraphMermaid === 'function') {
41
+ // Fallback
42
+ try { window.reloadGraphMermaid(); } catch{}
43
+ }
44
+ }).catch(()=>{});
45
+ } else if(section === 'instructions') {
46
+ // Ensure instructions module script loaded and then invoke loadInstructions directly as fail-safe.
47
+ ensureInstructionsModule().then(()=>{
48
+ if(typeof window.loadSectionData === 'function') window.loadSectionData(section);
49
+ if(typeof window.loadInstructions === 'function') {
50
+ try { window.loadInstructions(); } catch(e){ /* ignore */ }
51
+ }
52
+ }).catch(()=>{
53
+ if(typeof window.loadInstructions === 'function') {
54
+ try { window.loadInstructions(); } catch(e){ }
55
+ }
56
+ });
57
+ } else {
58
+ if (typeof window.loadSectionData === 'function') window.loadSectionData(section);
59
+ }
60
+ // Config panel auto-refresh lifecycle
61
+ if (section === 'config') {
62
+ if (typeof window.startConfigAutoRefresh === 'function') window.startConfigAutoRefresh();
63
+ } else {
64
+ if (typeof window.stopConfigAutoRefresh === 'function') window.stopConfigAutoRefresh();
65
+ }
66
+ }
67
+
68
+ // Ensure graph/drilldown heavy module is loaded only once.
69
+ let __graphModuleLoad = null;
70
+ function loadGraphModule(){
71
+ if(__graphModuleLoad) return __graphModuleLoad;
72
+ __graphModuleLoad = (async ()=>{
73
+ try{
74
+ // Scripts are already referenced with defer in admin.html; they may already have executed.
75
+ if(typeof window.reloadGraphMermaid === 'function' && typeof window.initGraphScopeDefaults === 'function') return;
76
+ // Attempt dynamic import fallback for environments that prefer it.
77
+ try { await import('./admin.graph.js'); } catch(e) { /* ignore import failures */ }
78
+ try { await import('./admin.drilldown.js'); } catch(e) { /* ignore */ }
79
+ }catch(e){ /* swallow */ }
80
+ })();
81
+ return __graphModuleLoad;
82
+ }
83
+
84
+ // Public ensure wrapper for symmetry / future retries
85
+ function ensureGraphModule(){ return loadGraphModule(); }
86
+
87
+ // Lightweight loader for instructions module in case defer script failed to execute before navigation.
88
+ let __instructionsModuleLoad = null;
89
+ function ensureInstructionsModule(){
90
+ if(typeof window.loadInstructions === 'function') return Promise.resolve();
91
+ if(__instructionsModuleLoad) return __instructionsModuleLoad;
92
+ __instructionsModuleLoad = (async ()=>{
93
+ // Attempt dynamic import; ignore errors (the static defer script may already exist or path may differ in dist build)
94
+ try { await import('./admin.instructions.js'); } catch(e) { /* ignore */ }
95
+ })();
96
+ return __instructionsModuleLoad;
97
+ }
98
+
99
+ function startAutoRefresh(){
100
+ if(window.__adminRefresh) clearInterval(window.__adminRefresh);
101
+ window.__adminRefresh = setInterval(()=>{
102
+ if(window.currentSection === 'overview' && typeof window.loadOverviewData === 'function') window.loadOverviewData();
103
+ if(window.currentSection === 'sessions' && typeof window.loadSessions === 'function') window.loadSessions();
104
+ if(window.currentSection === 'maintenance' && typeof window.loadMaintenanceStatus === 'function') window.loadMaintenanceStatus();
105
+ }, 30000);
106
+ }
107
+
108
+ document.addEventListener('DOMContentLoaded', function(){
109
+ // expose showSection for inline handlers expected by existing markup
110
+ window.showSection = showSection;
111
+ window.ensureGraphModule = ensureGraphModule;
112
+ window.startAutoRefresh = startAutoRefresh;
113
+ try { showSection('overview'); } catch(e){ /* ignore */ }
114
+ try { startAutoRefresh(); } catch(e){ /* ignore */ }
115
+ try { wireAdminControls(); } catch(e) { /* ignore wiring errors */ }
116
+ });
117
+
118
+ // Attach event listeners to commonly interacted controls. This reduces reliance on inline attributes.
119
+ function wireAdminControls(){
120
+ // Set defaults before any list rendering (avoid undefined page size causing slice logic oddities)
121
+ if(!window.instructionPageSize) window.instructionPageSize = 25;
122
+ // Wire nav buttons (previously relied on inline onclick attributes)
123
+ document.querySelectorAll('.nav-btn').forEach((btn) => {
124
+ try {
125
+ btn.addEventListener('click', (ev) => {
126
+ // Determine target section by button text or aria/data attribute if present
127
+ let target = btn.getAttribute('data-section');
128
+ if(!target){
129
+ const txt = (btn.textContent || '').toLowerCase();
130
+ if(txt.includes('overview')) target = 'overview';
131
+ else if(txt.includes('config')) target = 'config';
132
+ else if(txt.includes('session')) target = 'sessions';
133
+ else if(txt.includes('maintenance')) target = 'maintenance';
134
+ else if(txt.includes('monitor')) target = 'monitoring';
135
+ else if(txt.includes('instruction')) target = 'instructions';
136
+ else if(txt.includes('graph')) target = 'graph';
137
+ }
138
+ if(target) {
139
+ try { showSection(target); } catch(e){ /* ignore */ }
140
+ }
141
+ });
142
+ } catch(e){}
143
+ });
144
+
145
+ // Delegated fallback: if an event bubbles from a button inside .admin-nav, handle it.
146
+ const nav = document.querySelector('.admin-nav');
147
+ if(nav && !nav.__delegatedTabs){
148
+ nav.addEventListener('click', (e) => {
149
+ const targetEl = (e.target instanceof HTMLElement) ? (e.target.closest('.nav-btn')) : null;
150
+ if(!targetEl) return;
151
+ const section = targetEl.getAttribute('data-section');
152
+ if(section){
153
+ e.preventDefault();
154
+ try { showSection(section); } catch(err){ /* ignore */ }
155
+ }
156
+ });
157
+ Object.defineProperty(nav,'__delegatedTabs',{value:true, enumerable:false});
158
+ }
159
+
160
+ // Expose a small debug utility for manual diagnosis in browser console.
161
+ if(!window._debugTabs){
162
+ window._debugTabs = function(){
163
+ const buttons = Array.from(document.querySelectorAll('.nav-btn')).map(function(b){
164
+ return {
165
+ text: (b.textContent || '').trim(),
166
+ ds: b.getAttribute('data-section'),
167
+ hasClick: !!(b._listenerAttached),
168
+ classes: b.className
169
+ };
170
+ });
171
+ return { currentSection: window.currentSection, buttons: buttons };
172
+ };
173
+ }
174
+
175
+ // Ensure active state of nav buttons maps to currentSection correctly
176
+ const updateNavActive = () => {
177
+ document.querySelectorAll('.nav-btn').forEach(btn => {
178
+ const target = btn.getAttribute('data-section');
179
+ if(target === window.currentSection) btn.classList.add('active'); else btn.classList.remove('active');
180
+ });
181
+ };
182
+ // Periodically refresh nav active state in case other code changes currentSection
183
+ setInterval(updateNavActive, 600);
184
+
185
+ // Log tail toggle
186
+ const tailBtn = document.getElementById('log-tail-btn');
187
+ if(tailBtn) tailBtn.addEventListener('click', () => { if(typeof window.toggleLogTail === 'function') window.toggleLogTail(); });
188
+
189
+ // Log reload (if input for lines exists)
190
+ const linesInput = document.getElementById('log-lines');
191
+ if(linesInput) linesInput.addEventListener('change', () => { if(typeof window.loadLogs === 'function') window.loadLogs(); });
192
+
193
+ // Backup filter input
194
+ const backupSearch = document.getElementById('backup-search');
195
+ if(backupSearch) backupSearch.addEventListener('input', () => { if(typeof window.filterBackupRows === 'function') window.filterBackupRows(); });
196
+
197
+ // Delegate maintenance control buttons (enable/disable) inside maintenance-control
198
+ const maint = document.getElementById('maintenance-control');
199
+ if(maint) maint.addEventListener('click', (ev) => {
200
+ const el = ev.target;
201
+ if(!(el instanceof HTMLElement)) return;
202
+ if(el.matches('[data-toggle-maint]')){
203
+ const enable = el.getAttribute('data-toggle-maint') === '1';
204
+ if(typeof window.toggleMaintenanceMode === 'function') window.toggleMaintenanceMode(enable);
205
+ }
206
+ });
207
+
208
+ // System Operations action buttons delegation
209
+ const sysOps = document.querySelector('#maintenance-section .action-buttons');
210
+ if(sysOps && !sysOps.__sysOpsWired){
211
+ sysOps.addEventListener('click', (ev)=>{
212
+ const el = ev.target instanceof HTMLElement ? ev.target.closest('[data-op]') : null;
213
+ if(!el) return;
214
+ const op = el.getAttribute('data-op');
215
+ if(op === 'create-backup' && typeof window.performBackup === 'function') { try { window.performBackup(); } catch(_){} }
216
+ else if(op === 'clear-caches' && typeof window.clearCaches === 'function') { try { window.clearCaches(); } catch(_){} }
217
+ else if(op === 'restart-server' && typeof window.restartServer === 'function') { try { window.restartServer(); } catch(_){} }
218
+ else if(op === 'restore-backup') {
219
+ const sel = document.getElementById('backup-select');
220
+ if(sel && sel.value){
221
+ if(typeof window.restoreBackup === 'function') { try { window.restoreBackup(sel.value); } catch(_){} }
222
+ }
223
+ }
224
+ });
225
+ Object.defineProperty(sysOps,'__sysOpsWired',{value:true});
226
+ }
227
+ // Convert existing inline handlers to event listeners and observe future additions
228
+ convertInlineHandlers(document);
229
+ const mo = new MutationObserver((records) => {
230
+ for(const r of records){
231
+ r.addedNodes.forEach(n => {
232
+ if(!(n instanceof HTMLElement)) return;
233
+ convertInlineHandlers(n);
234
+ });
235
+ }
236
+ });
237
+ mo.observe(document.documentElement || document.body, { childList: true, subtree: true });
238
+ }
239
+
240
+ // Replace inline attributes (onclick, oninput, onsubmit) with proper event listeners.
241
+ // Security: Uses a named function dispatch map instead of new Function() (Issue #34 fix).
242
+ // Inline handlers must reference registered function names (e.g., onclick="handleSave(event)")
243
+ function safeExecInlineCode(el, code, event) {
244
+ // Extract function name and arguments from inline handler
245
+ // e.g., "editInstruction('000-bootstrapper')" or "handleSave(event)"
246
+ const match = code.match(/^([a-zA-Z_$][\w$.]*)\s*\(([^)]*)\)/);
247
+ if (match) {
248
+ const fnPath = match[1];
249
+ const argsStr = match[2].trim();
250
+ const parts = fnPath.split('.');
251
+ let fn = window;
252
+ for (const p of parts) {
253
+ if (fn == null) return;
254
+ fn = fn[p];
255
+ }
256
+ if (typeof fn === 'function') {
257
+ try {
258
+ // Parse inline arguments: resolve string literals, numbers, 'event', booleans, null
259
+ const args = argsStr ? argsStr.split(',').map(a => {
260
+ a = a.trim();
261
+ if (a === 'event' || a === 'e') return event;
262
+ if (a === 'this') return el;
263
+ if (a === 'true') return true;
264
+ if (a === 'false') return false;
265
+ if (a === 'null') return null;
266
+ if (a === 'undefined') return undefined;
267
+ // String literal (single or double quoted)
268
+ const strMatch = a.match(/^(['"])(.*)\1$/);
269
+ if (strMatch) return strMatch[2];
270
+ // Number
271
+ if (!isNaN(Number(a)) && a !== '') return Number(a);
272
+ // Fallback: treat as window property reference
273
+ return window[a];
274
+ }) : [];
275
+ fn.apply(el, args);
276
+ } catch (e) { console.error('inline handler failed:', e); }
277
+ return;
278
+ }
279
+ }
280
+ // Handle guard pattern: "window.fn && window.fn('arg')" or "fn && fn('arg')"
281
+ const guardMatch = code.match(/^[\w$.]+\s*&&\s*([a-zA-Z_$][\w$.]*)\s*\(([^)]*)\)/);
282
+ if (guardMatch) {
283
+ const fnPath2 = guardMatch[1];
284
+ const argsStr2 = guardMatch[2].trim();
285
+ const parts2 = fnPath2.split('.');
286
+ let fn2 = window;
287
+ for (const p of parts2) { if (fn2 == null) return; fn2 = fn2[p]; }
288
+ if (typeof fn2 === 'function') {
289
+ try {
290
+ const args2 = argsStr2 ? argsStr2.split(',').map(a => {
291
+ a = a.trim();
292
+ if (a === 'event' || a === 'e') return event;
293
+ if (a === 'this') return el;
294
+ const sm = a.match(/^(['"])(.*)\1$/);
295
+ if (sm) return sm[2];
296
+ if (!isNaN(Number(a)) && a !== '') return Number(a);
297
+ return window[a];
298
+ }) : [];
299
+ fn2.apply(el, args2);
300
+ } catch (e) { console.error('inline handler failed:', e); }
301
+ return;
302
+ }
303
+ }
304
+ // Fallback: simple known patterns
305
+ if (/^return\s+false\s*;?\s*$/.test(code)) { event.preventDefault(); return; }
306
+ if (/^(this\.(value|checked|disabled)\s*=)/.test(code)) return; // DOM assignment — skip
307
+ console.warn('[admin.boot] Unrecognized inline handler, skipped for security:', code.substring(0, 60));
308
+ }
309
+
310
+ function convertInlineHandlers(root){
311
+ if(!root || !(root.querySelectorAll)) return;
312
+ // onclick
313
+ root.querySelectorAll('[onclick]').forEach(el => {
314
+ try {
315
+ const code = el.getAttribute('onclick');
316
+ if(!code) return;
317
+ el.removeAttribute('onclick');
318
+ el.addEventListener('click', function(event){
319
+ safeExecInlineCode(this, code, event);
320
+ });
321
+ } catch(e){ /* ignore */ }
322
+ });
323
+ // oninput
324
+ root.querySelectorAll('[oninput]').forEach(el => {
325
+ try {
326
+ const code = el.getAttribute('onput') || el.getAttribute('oninput');
327
+ if(!code) return;
328
+ el.removeAttribute('oninput');
329
+ el.removeAttribute('onput');
330
+ el.addEventListener('input', function(event){
331
+ safeExecInlineCode(this, code, event);
332
+ });
333
+ } catch(e){ /* ignore */ }
334
+ });
335
+ // onchange
336
+ root.querySelectorAll('[onchange]').forEach(el => {
337
+ try {
338
+ const code = el.getAttribute('onchange');
339
+ if(!code) return;
340
+ el.removeAttribute('onchange');
341
+ el.addEventListener('change', function(event){
342
+ safeExecInlineCode(this, code, event);
343
+ });
344
+ } catch(e){ /* ignore */ }
345
+ });
346
+ // onsubmit (for forms) - execute and honor returned false
347
+ root.querySelectorAll('form[onsubmit]').forEach(form => {
348
+ try {
349
+ const code = form.getAttribute('onsubmit');
350
+ if(!code) return;
351
+ form.removeAttribute('onsubmit');
352
+ form.addEventListener('submit', function(event){
353
+ safeExecInlineCode(this, code, event);
354
+ });
355
+ } catch(e){ /* ignore */ }
356
+ });
357
+ }
358
+
359
+ })();
@@ -0,0 +1,196 @@
1
+ /* eslint-disable */
2
+ // Configuration panel – dynamic flags grouped by category with search, auto-refresh, and doc links
3
+ (function(){
4
+ var _configRefreshTimer = null;
5
+ var _collapsedCategories = {};
6
+
7
+ function escapeHtml(s) { return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }
8
+
9
+ function buildFlagRow(f, featureFlags) {
10
+ var isBool = f.type === 'boolean';
11
+ var currentVal = (f.enabled !== undefined ? (f.enabled ? 'on' : 'off') : (f.value !== undefined ? f.value : '')) || '';
12
+ var control = isBool
13
+ ? '<select data-flag="' + escapeHtml(f.name.toLowerCase()) + '" class="form-input cfg-flag-select">'
14
+ + '<option value="1"' + ((featureFlags[f.name.toLowerCase()] ?? f.enabled) ? ' selected' : '') + '>On</option>'
15
+ + '<option value="0"' + (!(featureFlags[f.name.toLowerCase()] ?? f.enabled) ? ' selected' : '') + '>Off</option>'
16
+ + '</select>'
17
+ : '<span class="cfg-flag-ro">' + escapeHtml(currentVal) + '</span>';
18
+ var stabClass = 'cfg-stab-' + (f.stability || 'stable');
19
+ var docHref = f.docAnchor ? ('https://github.com/jagilber-org/index-server/blob/main/docs/configuration.md#' + encodeURIComponent(f.docAnchor)) : '';
20
+ var docIcon = docHref ? ' <a class="cfg-doc-link" href="' + docHref + '" target="_blank" rel="noopener" title="Documentation for ' + escapeHtml(f.name) + '">📖</a>' : '';
21
+ return '<tr class="cfg-flag-row" data-flagname="' + escapeHtml(f.name.toLowerCase()) + '">'
22
+ + '<td class="cfg-flag-name">' + escapeHtml(f.name) + docIcon + '</td>'
23
+ + '<td>' + control + '</td>'
24
+ + '<td class="cfg-flag-default">' + escapeHtml(f.default || '') + '</td>'
25
+ + '<td class="' + stabClass + '">' + escapeHtml(f.stability || '') + '</td>'
26
+ + '<td class="cfg-flag-desc">' + escapeHtml(f.description || '') + '</td>'
27
+ + '</tr>';
28
+ }
29
+
30
+ function groupByCategory(flags) {
31
+ var groups = {};
32
+ var order = [];
33
+ flags.forEach(function(f) {
34
+ if (!groups[f.category]) { groups[f.category] = []; order.push(f.category); }
35
+ groups[f.category].push(f);
36
+ });
37
+ return { groups: groups, order: order };
38
+ }
39
+
40
+ function buildFlagsHtml(allFlags, featureFlags) {
41
+ if (!allFlags.length) return '<div class="cfg-no-flags">No feature flags detected</div>';
42
+ var catData = groupByCategory(allFlags);
43
+ var html = '<div class="cfg-flag-filter"><input type="text" id="cfg-flag-search" class="form-input" placeholder="Filter flags..." /></div>';
44
+ catData.order.forEach(function(cat) {
45
+ var collapsed = _collapsedCategories[cat];
46
+ var flags = catData.groups[cat];
47
+ html += '<div class="cfg-category-group" data-category="' + escapeHtml(cat) + '">';
48
+ html += '<div class="cfg-category-header" onclick="toggleConfigCategory(\'' + escapeHtml(cat) + '\')">';
49
+ html += '<span class="cfg-category-chevron">' + (collapsed ? 'â–¶' : 'â–¼') + '</span> ';
50
+ html += '<span class="cfg-category-name">' + escapeHtml(cat.charAt(0).toUpperCase() + cat.slice(1)) + '</span>';
51
+ html += ' <span class="cfg-category-count">(' + flags.length + ')</span>';
52
+ html += '</div>';
53
+ html += '<table class="cfg-flag-table"' + (collapsed ? ' style="display:none"' : '') + '>';
54
+ html += '<thead><tr><th>Flag</th><th>Value</th><th>Default</th><th>Stability</th><th>Description</th></tr></thead>';
55
+ html += '<tbody>';
56
+ flags.forEach(function(f) { html += buildFlagRow(f, featureFlags); });
57
+ html += '</tbody></table>';
58
+ html += '</div>';
59
+ });
60
+ return html;
61
+ }
62
+
63
+ async function loadConfiguration() {
64
+ try {
65
+ var res = await fetch('/api/admin/config');
66
+ var data = await res.json();
67
+ if (!data.success) throw new Error('Failed to load config');
68
+ var cfg = data.config;
69
+ var featureFlags = data.featureFlags || {};
70
+ var allFlags = Array.isArray(data.allFlags) ? data.allFlags : [];
71
+ if (!allFlags.length) {
72
+ try {
73
+ var fres = await fetch('/api/admin/flags');
74
+ var fdata = await fres.json();
75
+ if (fdata.success && Array.isArray(fdata.allFlags)) allFlags = fdata.allFlags;
76
+ } catch(e) { /* ignore */ }
77
+ }
78
+ var refreshedAt = data.timestamp ? new Date(data.timestamp).toLocaleTimeString() : new Date().toLocaleTimeString();
79
+
80
+ var html = '<div class="cfg-panel">'
81
+ + '<form onsubmit="return updateConfiguration(event)" class="cfg-server-form">'
82
+ + '<div class="cfg-server-grid">'
83
+ + '<div class="form-group"><label class="form-label">Max Connections</label>'
84
+ + '<input class="form-input" type="number" id="cfg-maxConnections" value="' + cfg.serverSettings.maxConnections + '" /></div>'
85
+ + '<div class="form-group"><label class="form-label">Request Timeout (ms)</label>'
86
+ + '<input class="form-input" type="number" id="cfg-requestTimeout" value="' + cfg.serverSettings.requestTimeout + '" /></div>'
87
+ + '<div class="form-group"><label class="form-label">Verbose Logging</label>'
88
+ + '<select class="form-input" id="cfg-verbose"><option value="1"' + (cfg.serverSettings.enableVerboseLogging ? ' selected' : '') + '>Enabled</option>'
89
+ + '<option value="0"' + (!cfg.serverSettings.enableVerboseLogging ? ' selected' : '') + '>Disabled</option></select></div>'
90
+ + '<div class="form-group"><label class="form-label">Enable Mutation</label>'
91
+ + '<select class="form-input" id="cfg-mutation"><option value="1"' + (cfg.serverSettings.enableMutation ? ' selected' : '') + '>Enabled</option>'
92
+ + '<option value="0"' + (!cfg.serverSettings.enableMutation ? ' selected' : '') + '>Disabled</option></select></div>'
93
+ + '<div class="form-group"><label class="form-label">Rate Limit Window (ms)</label>'
94
+ + '<input class="form-input" type="number" id="cfg-windowMs" value="' + cfg.serverSettings.rateLimit.windowMs + '" /></div>'
95
+ + '<div class="form-group"><label class="form-label">Rate Limit Max Requests</label>'
96
+ + '<input class="form-input" type="number" id="cfg-maxRequests" value="' + cfg.serverSettings.rateLimit.maxRequests + '" /></div>'
97
+ + '</div>'
98
+ + '<div class="cfg-save-row"><button class="action-btn" type="submit">💾 Save Config</button></div>'
99
+ + '</form>'
100
+ + '<div class="cfg-flags-section">'
101
+ + '<div class="cfg-flags-header">'
102
+ + '<h3 class="cfg-flags-title">Feature Flags</h3>'
103
+ + '<span class="cfg-refreshed">Last refreshed: ' + escapeHtml(refreshedAt) + '</span>'
104
+ + '</div>'
105
+ + '<div class="cfg-flags-note">All recognized flags grouped by category. Edit boolean flags inline – changes persist to file. Non-boolean flags are read-only.</div>'
106
+ + buildFlagsHtml(allFlags, featureFlags)
107
+ + '</div></div>';
108
+
109
+ var target = document.getElementById('config-form');
110
+ if (target) {
111
+ target.innerHTML = html;
112
+ target.classList.remove('loading');
113
+ var searchInput = document.getElementById('cfg-flag-search');
114
+ if (searchInput) searchInput.addEventListener('input', filterConfigFlags);
115
+ }
116
+ } catch (e) {
117
+ var target = document.getElementById('config-form');
118
+ if (target) target.innerHTML = '<div class="error">Failed to load configuration</div>';
119
+ }
120
+ }
121
+
122
+ function filterConfigFlags() {
123
+ var term = (document.getElementById('cfg-flag-search') || {}).value;
124
+ if (term === undefined) return;
125
+ term = term.toLowerCase();
126
+ var rows = document.querySelectorAll('.cfg-flag-row');
127
+ rows.forEach(function(row) {
128
+ var name = row.getAttribute('data-flagname') || '';
129
+ var desc = (row.querySelector('.cfg-flag-desc') || {}).textContent || '';
130
+ row.style.display = (name.indexOf(term) !== -1 || desc.toLowerCase().indexOf(term) !== -1) ? '' : 'none';
131
+ });
132
+ // Show all category groups when filtering
133
+ if (term) {
134
+ document.querySelectorAll('.cfg-flag-table').forEach(function(t) { t.style.display = ''; });
135
+ }
136
+ }
137
+
138
+ function toggleConfigCategory(cat) {
139
+ _collapsedCategories[cat] = !_collapsedCategories[cat];
140
+ var group = document.querySelector('.cfg-category-group[data-category="' + cat + '"]');
141
+ if (!group) return;
142
+ var table = group.querySelector('.cfg-flag-table');
143
+ var chevron = group.querySelector('.cfg-category-chevron');
144
+ if (table) table.style.display = _collapsedCategories[cat] ? 'none' : '';
145
+ if (chevron) chevron.textContent = _collapsedCategories[cat] ? 'â–¶' : 'â–¼';
146
+ }
147
+
148
+ function startConfigAutoRefresh() {
149
+ stopConfigAutoRefresh();
150
+ _configRefreshTimer = setInterval(function() {
151
+ // Only auto-refresh if config section is visible
152
+ var section = document.getElementById('config-section');
153
+ if (section && !section.classList.contains('hidden')) loadConfiguration();
154
+ }, 15000);
155
+ }
156
+
157
+ function stopConfigAutoRefresh() {
158
+ if (_configRefreshTimer) { clearInterval(_configRefreshTimer); _configRefreshTimer = null; }
159
+ }
160
+
161
+ async function updateConfiguration(ev) {
162
+ ev.preventDefault();
163
+ var flagSelects = document.querySelectorAll('[data-flag]');
164
+ var featureFlags = {};
165
+ flagSelects.forEach(function(sel) {
166
+ var name = sel.getAttribute('data-flag');
167
+ if (name) featureFlags[name] = sel.value === '1';
168
+ });
169
+ var updates = {
170
+ serverSettings: {
171
+ maxConnections: parseInt(document.getElementById('cfg-maxConnections').value),
172
+ requestTimeout: parseInt(document.getElementById('cfg-requestTimeout').value),
173
+ enableVerboseLogging: document.getElementById('cfg-verbose').value === '1',
174
+ enableMutation: document.getElementById('cfg-mutation').value === '1',
175
+ rateLimit: {
176
+ windowMs: parseInt(document.getElementById('cfg-windowMs').value),
177
+ maxRequests: parseInt(document.getElementById('cfg-maxRequests').value)
178
+ }
179
+ },
180
+ featureFlags: featureFlags
181
+ };
182
+ try {
183
+ var res = await fetch('/api/admin/config', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(updates)});
184
+ var data = await res.json();
185
+ if (data.success) { if (typeof showSuccess === 'function') showSuccess('Configuration updated'); loadConfiguration(); }
186
+ else { if (typeof showError === 'function') showError(data.error || 'Update failed'); }
187
+ } catch (e) { if (typeof showError === 'function') showError('Update failed'); }
188
+ return false;
189
+ }
190
+
191
+ window.loadConfiguration = loadConfiguration;
192
+ window.updateConfiguration = updateConfiguration;
193
+ window.toggleConfigCategory = toggleConfigCategory;
194
+ window.startConfigAutoRefresh = startConfigAutoRefresh;
195
+ window.stopConfigAutoRefresh = stopConfigAutoRefresh;
196
+ })();