aios-core 4.2.15 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. package/.aios-core/cli/commands/validate/index.js +1 -1
  2. package/.aios-core/core/code-intel/code-intel-client.js +19 -5
  3. package/.aios-core/core/code-intel/helpers/creation-helper.js +183 -0
  4. package/.aios-core/core/code-intel/helpers/devops-helper.js +166 -0
  5. package/.aios-core/core/code-intel/helpers/planning-helper.js +248 -0
  6. package/.aios-core/core/code-intel/helpers/qa-helper.js +187 -0
  7. package/.aios-core/core/code-intel/helpers/story-helper.js +146 -0
  8. package/.aios-core/core/code-intel/hook-runtime.js +186 -0
  9. package/.aios-core/core/code-intel/index.js +2 -0
  10. package/.aios-core/core/code-intel/providers/code-graph-provider.js +8 -0
  11. package/.aios-core/core/code-intel/providers/provider-interface.js +9 -0
  12. package/.aios-core/core/code-intel/providers/registry-provider.js +515 -0
  13. package/.aios-core/core/config/schemas/framework-config.schema.json +155 -7
  14. package/.aios-core/core/config/schemas/project-config.schema.json +329 -15
  15. package/.aios-core/core/config/template-overrides.js +84 -0
  16. package/.aios-core/core/docs/troubleshooting-guide.md +1 -1
  17. package/.aios-core/core/doctor/checks/agent-memory.js +63 -0
  18. package/.aios-core/core/doctor/checks/claude-md.js +56 -0
  19. package/.aios-core/core/doctor/checks/code-intel.js +131 -0
  20. package/.aios-core/core/doctor/checks/commands-count.js +81 -0
  21. package/.aios-core/core/doctor/checks/core-config.js +53 -0
  22. package/.aios-core/core/doctor/checks/entity-registry.js +53 -0
  23. package/.aios-core/core/doctor/checks/git-hooks.js +50 -0
  24. package/.aios-core/core/doctor/checks/graph-dashboard.js +48 -0
  25. package/.aios-core/core/doctor/checks/hooks-claude-count.js +118 -0
  26. package/.aios-core/core/doctor/checks/ide-sync.js +85 -0
  27. package/.aios-core/core/doctor/checks/index.js +46 -0
  28. package/.aios-core/core/doctor/checks/node-version.js +33 -0
  29. package/.aios-core/core/doctor/checks/npm-packages.js +35 -0
  30. package/.aios-core/core/doctor/checks/rules-files.js +61 -0
  31. package/.aios-core/core/doctor/checks/settings-json.js +121 -0
  32. package/.aios-core/core/doctor/checks/skills-count.js +72 -0
  33. package/.aios-core/core/doctor/fix-handler.js +165 -0
  34. package/.aios-core/core/doctor/formatters/json.js +14 -0
  35. package/.aios-core/core/doctor/formatters/text.js +59 -0
  36. package/.aios-core/core/doctor/index.js +94 -0
  37. package/.aios-core/core/graph-dashboard/cli.js +361 -0
  38. package/.aios-core/core/graph-dashboard/data-sources/code-intel-source.js +234 -0
  39. package/.aios-core/core/graph-dashboard/data-sources/metrics-source.js +95 -0
  40. package/.aios-core/core/graph-dashboard/data-sources/registry-source.js +106 -0
  41. package/.aios-core/core/graph-dashboard/formatters/dot-formatter.js +45 -0
  42. package/.aios-core/core/graph-dashboard/formatters/html-formatter.js +1437 -0
  43. package/.aios-core/core/graph-dashboard/formatters/json-formatter.js +13 -0
  44. package/.aios-core/core/graph-dashboard/formatters/mermaid-formatter.js +59 -0
  45. package/.aios-core/core/graph-dashboard/index.js +21 -0
  46. package/.aios-core/core/graph-dashboard/renderers/stats-renderer.js +217 -0
  47. package/.aios-core/core/graph-dashboard/renderers/status-renderer.js +125 -0
  48. package/.aios-core/core/graph-dashboard/renderers/tree-renderer.js +119 -0
  49. package/.aios-core/core/health-check/base-check.js +1 -1
  50. package/.aios-core/core/health-check/check-registry.js +1 -1
  51. package/.aios-core/core/health-check/checks/deployment/build-config.js +1 -1
  52. package/.aios-core/core/health-check/checks/deployment/ci-config.js +1 -1
  53. package/.aios-core/core/health-check/checks/deployment/deployment-readiness.js +1 -1
  54. package/.aios-core/core/health-check/checks/deployment/docker-config.js +1 -1
  55. package/.aios-core/core/health-check/checks/deployment/env-file.js +1 -1
  56. package/.aios-core/core/health-check/checks/deployment/index.js +1 -1
  57. package/.aios-core/core/health-check/checks/index.js +1 -1
  58. package/.aios-core/core/health-check/checks/local/disk-space.js +1 -1
  59. package/.aios-core/core/health-check/checks/local/environment-vars.js +1 -1
  60. package/.aios-core/core/health-check/checks/local/git-install.js +1 -1
  61. package/.aios-core/core/health-check/checks/local/ide-detection.js +1 -1
  62. package/.aios-core/core/health-check/checks/local/index.js +1 -1
  63. package/.aios-core/core/health-check/checks/local/memory.js +1 -1
  64. package/.aios-core/core/health-check/checks/local/network.js +1 -1
  65. package/.aios-core/core/health-check/checks/local/npm-install.js +1 -1
  66. package/.aios-core/core/health-check/checks/local/shell-environment.js +1 -1
  67. package/.aios-core/core/health-check/checks/project/agent-config.js +1 -1
  68. package/.aios-core/core/health-check/checks/project/aios-directory.js +1 -1
  69. package/.aios-core/core/health-check/checks/project/dependencies.js +1 -1
  70. package/.aios-core/core/health-check/checks/project/framework-config.js +1 -1
  71. package/.aios-core/core/health-check/checks/project/index.js +1 -1
  72. package/.aios-core/core/health-check/checks/project/node-version.js +1 -1
  73. package/.aios-core/core/health-check/checks/project/package-json.js +1 -1
  74. package/.aios-core/core/health-check/checks/project/task-definitions.js +1 -1
  75. package/.aios-core/core/health-check/checks/project/workflow-dependencies.js +1 -1
  76. package/.aios-core/core/health-check/checks/repository/branch-protection.js +1 -1
  77. package/.aios-core/core/health-check/checks/repository/commit-history.js +1 -1
  78. package/.aios-core/core/health-check/checks/repository/conflicts.js +1 -1
  79. package/.aios-core/core/health-check/checks/repository/git-repo.js +1 -1
  80. package/.aios-core/core/health-check/checks/repository/git-status.js +1 -1
  81. package/.aios-core/core/health-check/checks/repository/gitignore.js +1 -1
  82. package/.aios-core/core/health-check/checks/repository/index.js +1 -1
  83. package/.aios-core/core/health-check/checks/repository/large-files.js +1 -1
  84. package/.aios-core/core/health-check/checks/repository/lockfile-integrity.js +1 -1
  85. package/.aios-core/core/health-check/checks/services/api-endpoints.js +1 -1
  86. package/.aios-core/core/health-check/checks/services/claude-code.js +1 -1
  87. package/.aios-core/core/health-check/checks/services/gemini-cli.js +1 -1
  88. package/.aios-core/core/health-check/checks/services/github-cli.js +1 -1
  89. package/.aios-core/core/health-check/checks/services/index.js +1 -1
  90. package/.aios-core/core/health-check/checks/services/mcp-integration.js +1 -1
  91. package/.aios-core/core/health-check/engine.js +1 -1
  92. package/.aios-core/core/health-check/healers/backup-manager.js +1 -1
  93. package/.aios-core/core/health-check/healers/index.js +1 -1
  94. package/.aios-core/core/health-check/index.js +9 -2
  95. package/.aios-core/core/health-check/reporters/console.js +1 -1
  96. package/.aios-core/core/health-check/reporters/index.js +1 -1
  97. package/.aios-core/core/health-check/reporters/json.js +1 -1
  98. package/.aios-core/core/health-check/reporters/markdown.js +1 -1
  99. package/.aios-core/core/ids/layer-classifier.js +65 -0
  100. package/.aios-core/core/ids/registry-updater.js +49 -0
  101. package/.aios-core/core/index.esm.js +1 -1
  102. package/.aios-core/core/index.js +1 -1
  103. package/.aios-core/core/session/context-detector.js +2 -7
  104. package/.aios-core/core/synapse/context/context-tracker.js +9 -1
  105. package/.aios-core/core/synapse/engine.js +33 -13
  106. package/.aios-core/core/synapse/memory/memory-bridge.js +17 -43
  107. package/.aios-core/core/synapse/memory/synapse-memory-provider.js +201 -0
  108. package/.aios-core/core/synapse/runtime/hook-runtime.js +40 -2
  109. package/.aios-core/core/synapse/session/session-manager.js +3 -2
  110. package/.aios-core/core/synapse/utils/atomic-write.js +79 -0
  111. package/.aios-core/core-config.yaml +34 -1
  112. package/.aios-core/data/aios-kb.md +2 -2
  113. package/.aios-core/data/capability-detection.js +290 -0
  114. package/.aios-core/data/entity-registry.yaml +10450 -2129
  115. package/.aios-core/data/mcp-discipline.js +166 -0
  116. package/.aios-core/data/mcp-tool-examples.yaml +215 -0
  117. package/.aios-core/data/tok2-validation.js +168 -0
  118. package/.aios-core/data/tok3-token-comparison.js +123 -0
  119. package/.aios-core/data/tool-registry.yaml +648 -0
  120. package/.aios-core/data/tool-search-validation.js +174 -0
  121. package/.aios-core/data/workflow-chains.yaml +156 -0
  122. package/.aios-core/development/agents/aios-master.md +17 -10
  123. package/.aios-core/development/agents/analyst/MEMORY.md +33 -0
  124. package/.aios-core/development/agents/analyst.md +17 -10
  125. package/.aios-core/development/agents/architect/MEMORY.md +39 -0
  126. package/.aios-core/development/agents/architect.md +17 -10
  127. package/.aios-core/development/agents/data-engineer/MEMORY.md +32 -0
  128. package/.aios-core/development/agents/data-engineer.md +17 -10
  129. package/.aios-core/development/agents/dev/MEMORY.md +46 -0
  130. package/.aios-core/development/agents/dev.md +18 -11
  131. package/.aios-core/development/agents/devops/MEMORY.md +39 -0
  132. package/.aios-core/development/agents/devops.md +44 -10
  133. package/.aios-core/development/agents/pm/MEMORY.md +38 -0
  134. package/.aios-core/development/agents/pm.md +17 -10
  135. package/.aios-core/development/agents/po/MEMORY.md +45 -0
  136. package/.aios-core/development/agents/po.md +17 -10
  137. package/.aios-core/development/agents/qa/MEMORY.md +42 -0
  138. package/.aios-core/development/agents/qa.md +18 -11
  139. package/.aios-core/development/agents/sm/MEMORY.md +31 -0
  140. package/.aios-core/development/agents/sm.md +17 -10
  141. package/.aios-core/development/agents/squad-creator.md +18 -9
  142. package/.aios-core/development/agents/ux/MEMORY.md +31 -0
  143. package/.aios-core/development/agents/ux-design-expert.md +16 -9
  144. package/.aios-core/development/checklists/issue-triage-checklist.md +35 -0
  145. package/.aios-core/development/checklists/memory-audit-checklist.md +53 -0
  146. package/.aios-core/development/scripts/issue-triage.js +171 -0
  147. package/.aios-core/development/scripts/populate-entity-registry.js +412 -19
  148. package/.aios-core/development/scripts/unified-activation-pipeline.js +31 -10
  149. package/.aios-core/development/tasks/analyze-project-structure.md +48 -0
  150. package/.aios-core/development/tasks/apply-qa-fixes.md +7 -0
  151. package/.aios-core/development/tasks/architect-analyze-impact.md +8 -1
  152. package/.aios-core/development/tasks/brownfield-create-epic.md +41 -0
  153. package/.aios-core/development/tasks/brownfield-create-story.md +7 -0
  154. package/.aios-core/development/tasks/build-autonomous.md +7 -0
  155. package/.aios-core/development/tasks/create-deep-research-prompt.md +7 -0
  156. package/.aios-core/development/tasks/create-doc.md +44 -0
  157. package/.aios-core/development/tasks/create-next-story.md +17 -0
  158. package/.aios-core/development/tasks/create-suite.md +7 -0
  159. package/.aios-core/development/tasks/dev-develop-story.md +9 -1
  160. package/.aios-core/development/tasks/execute-checklist.md +7 -0
  161. package/.aios-core/development/tasks/github-devops-github-pr-automation.md +56 -0
  162. package/.aios-core/development/tasks/github-devops-pre-push-quality-gate.md +70 -0
  163. package/.aios-core/development/tasks/github-issue-triage.md +118 -0
  164. package/.aios-core/development/tasks/health-check.yaml +206 -171
  165. package/.aios-core/development/tasks/kb-mode-interaction.md +3 -3
  166. package/.aios-core/development/tasks/plan-create-context.md +47 -1
  167. package/.aios-core/development/tasks/plan-create-implementation.md +55 -0
  168. package/.aios-core/development/tasks/po-close-story.md +7 -0
  169. package/.aios-core/development/tasks/pr-automation.md +5 -5
  170. package/.aios-core/development/tasks/qa-create-fix-request.md +7 -0
  171. package/.aios-core/development/tasks/qa-fix-issues.md +7 -0
  172. package/.aios-core/development/tasks/qa-gate.md +56 -0
  173. package/.aios-core/development/tasks/qa-review-story.md +32 -1
  174. package/.aios-core/development/tasks/release-management.md +7 -0
  175. package/.aios-core/development/tasks/resolve-github-issue.md +608 -0
  176. package/.aios-core/development/tasks/review-contributor-pr.md +152 -0
  177. package/.aios-core/development/tasks/setup-llm-routing.md +1 -1
  178. package/.aios-core/development/tasks/spec-critique.md +8 -0
  179. package/.aios-core/development/tasks/spec-gather-requirements.md +7 -0
  180. package/.aios-core/development/tasks/spec-research-dependencies.md +4 -0
  181. package/.aios-core/development/tasks/spec-write-spec.md +5 -0
  182. package/.aios-core/development/tasks/triage-github-issues.md +356 -0
  183. package/.aios-core/development/tasks/validate-agents.md +4 -0
  184. package/.aios-core/development/tasks/validate-next-story.md +17 -0
  185. package/.aios-core/development/templates/agent-handoff-tmpl.yaml +48 -0
  186. package/.aios-core/development/templates/code-intel-integration-pattern.md +199 -0
  187. package/.aios-core/development/templates/ptc-entity-validation.md +113 -0
  188. package/.aios-core/development/templates/ptc-qa-gate.md +100 -0
  189. package/.aios-core/development/templates/ptc-research-aggregation.md +94 -0
  190. package/.aios-core/development/templates/service-template/README.md.hbs +158 -158
  191. package/.aios-core/development/templates/service-template/__tests__/index.test.ts.hbs +237 -237
  192. package/.aios-core/development/templates/service-template/client.ts.hbs +403 -403
  193. package/.aios-core/development/templates/service-template/errors.ts.hbs +182 -182
  194. package/.aios-core/development/templates/service-template/index.ts.hbs +120 -120
  195. package/.aios-core/development/templates/service-template/package.json.hbs +87 -87
  196. package/.aios-core/development/templates/service-template/types.ts.hbs +145 -145
  197. package/.aios-core/development/templates/squad/agent-template.md +11 -0
  198. package/.aios-core/development/templates/squad/task-template.md +21 -0
  199. package/.aios-core/development/templates/squad-template/LICENSE +21 -21
  200. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-COMPLETE.md +1 -1
  201. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.2-SUMMARY.md +1 -1
  202. package/.aios-core/framework-config.yaml +8 -0
  203. package/.aios-core/index.esm.js +1 -1
  204. package/.aios-core/index.js +1 -1
  205. package/.aios-core/infrastructure/integrations/ai-providers/index.js +1 -1
  206. package/.aios-core/infrastructure/schemas/task-v3-schema.json +6 -0
  207. package/.aios-core/infrastructure/scripts/collect-tool-usage.js +311 -0
  208. package/.aios-core/infrastructure/scripts/generate-optimization-report.js +497 -0
  209. package/.aios-core/infrastructure/scripts/generate-settings-json.js +300 -0
  210. package/.aios-core/infrastructure/scripts/git-config-detector.js +65 -9
  211. package/.aios-core/infrastructure/scripts/ide-sync/index.js +3 -1
  212. package/.aios-core/infrastructure/scripts/ide-sync/transformers/github-copilot.js +184 -0
  213. package/.aios-core/infrastructure/scripts/repository-detector.js +3 -3
  214. package/.aios-core/infrastructure/templates/aios-sync.yaml.template +182 -182
  215. package/.aios-core/infrastructure/templates/coderabbit.yaml.template +279 -279
  216. package/.aios-core/infrastructure/templates/github-workflows/ci.yml.template +169 -169
  217. package/.aios-core/infrastructure/templates/github-workflows/pr-automation.yml.template +330 -330
  218. package/.aios-core/infrastructure/templates/github-workflows/release.yml.template +196 -196
  219. package/.aios-core/infrastructure/templates/gitignore/gitignore-aios-base.tmpl +63 -63
  220. package/.aios-core/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +18 -18
  221. package/.aios-core/infrastructure/templates/gitignore/gitignore-node.tmpl +85 -85
  222. package/.aios-core/infrastructure/templates/gitignore/gitignore-python.tmpl +145 -145
  223. package/.aios-core/install-manifest.yaml +613 -305
  224. package/.aios-core/lib/build.json +1 -0
  225. package/.aios-core/local-config.yaml.template +71 -71
  226. package/.aios-core/monitor/hooks/lib/__init__.py +1 -1
  227. package/.aios-core/monitor/hooks/lib/enrich.py +58 -58
  228. package/.aios-core/monitor/hooks/lib/send_event.py +47 -47
  229. package/.aios-core/monitor/hooks/notification.py +29 -29
  230. package/.aios-core/monitor/hooks/post_tool_use.py +45 -45
  231. package/.aios-core/monitor/hooks/pre_compact.py +29 -29
  232. package/.aios-core/monitor/hooks/pre_tool_use.py +40 -40
  233. package/.aios-core/monitor/hooks/stop.py +29 -29
  234. package/.aios-core/monitor/hooks/subagent_stop.py +29 -29
  235. package/.aios-core/monitor/hooks/user_prompt_submit.py +38 -38
  236. package/.aios-core/product/templates/adr.hbs +125 -125
  237. package/.aios-core/product/templates/dbdr.hbs +241 -241
  238. package/.aios-core/product/templates/epic.hbs +212 -212
  239. package/.aios-core/product/templates/ide-rules/claude-rules.md +125 -0
  240. package/.aios-core/product/templates/pmdr.hbs +186 -186
  241. package/.aios-core/product/templates/prd-v2.0.hbs +216 -216
  242. package/.aios-core/product/templates/prd.hbs +201 -201
  243. package/.aios-core/product/templates/story.hbs +263 -263
  244. package/.aios-core/product/templates/task.hbs +170 -170
  245. package/.aios-core/product/templates/tmpl-comment-on-examples.sql +158 -158
  246. package/.aios-core/product/templates/tmpl-migration-script.sql +91 -91
  247. package/.aios-core/product/templates/tmpl-rls-granular-policies.sql +104 -104
  248. package/.aios-core/product/templates/tmpl-rls-kiss-policy.sql +10 -10
  249. package/.aios-core/product/templates/tmpl-rls-roles.sql +135 -135
  250. package/.aios-core/product/templates/tmpl-rls-simple.sql +77 -77
  251. package/.aios-core/product/templates/tmpl-rls-tenant.sql +152 -152
  252. package/.aios-core/product/templates/tmpl-rollback-script.sql +77 -77
  253. package/.aios-core/product/templates/tmpl-seed-data.sql +140 -140
  254. package/.aios-core/product/templates/tmpl-smoke-test.sql +16 -16
  255. package/.aios-core/product/templates/tmpl-staging-copy-merge.sql +139 -139
  256. package/.aios-core/product/templates/tmpl-stored-proc.sql +140 -140
  257. package/.aios-core/product/templates/tmpl-trigger.sql +152 -152
  258. package/.aios-core/product/templates/tmpl-view-materialized.sql +133 -133
  259. package/.aios-core/product/templates/tmpl-view.sql +177 -177
  260. package/.aios-core/scripts/pm.sh +0 -0
  261. package/.aios-core/user-guide.md +15 -15
  262. package/.aios-core/utils/filters/constants.js +10 -0
  263. package/.aios-core/utils/filters/content-filter.js +223 -0
  264. package/.aios-core/utils/filters/field-filter.js +126 -0
  265. package/.aios-core/utils/filters/index.js +180 -0
  266. package/.aios-core/utils/filters/schema-filter.js +157 -0
  267. package/.claude/CLAUDE.md +62 -0
  268. package/.claude/hooks/enforce-architecture-first.py +196 -196
  269. package/.claude/hooks/enforce-git-push-authority.sh +33 -0
  270. package/.claude/hooks/mind-clone-governance.py +192 -192
  271. package/.claude/hooks/read-protection.py +151 -151
  272. package/.claude/hooks/slug-validation.py +176 -176
  273. package/.claude/hooks/sql-governance.py +182 -182
  274. package/.claude/hooks/synapse-engine.cjs +28 -5
  275. package/.claude/hooks/write-path-validation.py +194 -194
  276. package/.claude/rules/agent-authority.md +105 -0
  277. package/.claude/rules/agent-handoff.md +97 -0
  278. package/.claude/rules/agent-memory-imports.md +15 -0
  279. package/.claude/rules/coderabbit-integration.md +101 -0
  280. package/.claude/rules/ids-principles.md +119 -0
  281. package/.claude/rules/story-lifecycle.md +145 -0
  282. package/.claude/rules/tool-examples.md +64 -0
  283. package/.claude/rules/tool-response-filtering.md +57 -0
  284. package/.claude/rules/workflow-execution.md +150 -0
  285. package/LICENSE +33 -33
  286. package/bin/aios-graph.js +9 -0
  287. package/bin/aios-init.js +2 -2
  288. package/bin/aios-minimal.js +0 -0
  289. package/bin/aios.js +17 -221
  290. package/bin/utils/detect-fsmonitor.js +70 -0
  291. package/bin/utils/framework-guard.js +238 -0
  292. package/bin/utils/validate-publish.js +108 -0
  293. package/package.json +6 -3
  294. package/packages/aios-install/bin/aios-install.js +0 -0
  295. package/packages/aios-install/bin/edmcp.js +0 -0
  296. package/packages/aios-pro-cli/bin/aios-pro.js +2 -0
  297. package/packages/installer/src/config/templates/core-config-template.js +25 -0
  298. package/packages/installer/src/installer/brownfield-upgrader.js +68 -5
  299. package/packages/installer/src/merger/index.js +3 -0
  300. package/packages/installer/src/merger/strategies/index.js +6 -0
  301. package/packages/installer/src/merger/strategies/yaml-merger.js +181 -0
  302. package/packages/installer/src/updater/index.js +4 -4
  303. package/packages/installer/src/wizard/i18n.js +321 -3
  304. package/packages/installer/src/wizard/ide-config-generator.js +173 -25
  305. package/packages/installer/src/wizard/index.js +119 -1
  306. package/packages/installer/src/wizard/pro-setup.js +137 -121
  307. package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +271 -0
  308. package/packages/installer/tests/unit/claude-md-template-v5/claude-md-template-v5.test.js +192 -0
  309. package/packages/installer/tests/unit/doctor/doctor-checks.test.js +610 -0
  310. package/packages/installer/tests/unit/doctor/doctor-orchestrator.test.js +134 -0
  311. package/packages/installer/tests/unit/entity-registry-bootstrap.test.js +186 -0
  312. package/packages/installer/tests/unit/generate-settings-json/generate-settings-json.test.js +309 -0
  313. package/packages/installer/tests/unit/ide-sync-integration/ide-sync-integration.test.js +230 -0
  314. package/packages/installer/tests/unit/merger/strategies.test.js +2 -2
  315. package/packages/installer/tests/unit/merger/yaml-merger.test.js +327 -0
  316. package/scripts/check-markdown-links.py +352 -352
  317. package/scripts/dashboard-parallel-dev.sh +0 -0
  318. package/scripts/dashboard-parallel-phase3.sh +0 -0
  319. package/scripts/dashboard-parallel-phase4.sh +0 -0
  320. package/scripts/install-monitor-hooks.sh +0 -0
  321. package/scripts/package-synapse.js +2 -1
  322. package/pro/README.md +0 -66
  323. package/pro/license/degradation.js +0 -220
  324. package/pro/license/errors.js +0 -450
  325. package/pro/license/feature-gate.js +0 -354
  326. package/pro/license/index.js +0 -181
  327. package/pro/license/license-api.js +0 -651
  328. package/pro/license/license-cache.js +0 -523
  329. package/pro/license/license-crypto.js +0 -303
@@ -0,0 +1,361 @@
1
+ 'use strict';
2
+
3
+ const { CodeIntelSource } = require('./data-sources/code-intel-source');
4
+ const { RegistrySource } = require('./data-sources/registry-source');
5
+ const { MetricsSource } = require('./data-sources/metrics-source');
6
+ const { renderTree } = require('./renderers/tree-renderer');
7
+ const { renderStats } = require('./renderers/stats-renderer');
8
+ const { renderStatus } = require('./renderers/status-renderer');
9
+ const { formatAsJson } = require('./formatters/json-formatter');
10
+ const { formatAsDot } = require('./formatters/dot-formatter');
11
+ const { formatAsMermaid } = require('./formatters/mermaid-formatter');
12
+ const { formatAsHtml } = require('./formatters/html-formatter');
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const { exec } = require('child_process');
17
+
18
+ const MAX_SUMMARY_PER_CATEGORY = 5;
19
+ const DEFAULT_WATCH_INTERVAL_MS = 5000;
20
+ const DEBOUNCE_MS = 300;
21
+
22
+ const FORMAT_MAP = {
23
+ json: formatAsJson,
24
+ dot: formatAsDot,
25
+ mermaid: formatAsMermaid,
26
+ html: formatAsHtml,
27
+ };
28
+
29
+ const VALID_FORMATS = ['ascii', ...Object.keys(FORMAT_MAP)];
30
+
31
+ const WATCH_FORMAT_MAP = {
32
+ dot: { formatter: formatAsDot, filename: 'graph.dot' },
33
+ mermaid: { formatter: formatAsMermaid, filename: 'graph.mmd' },
34
+ html: {
35
+ formatter: (graphData) => formatAsHtml(graphData, { autoRefresh: true, refreshInterval: 5 }),
36
+ filename: 'graph.html',
37
+ },
38
+ };
39
+
40
+ const COMMANDS = {
41
+ '--deps': handleDeps,
42
+ '--stats': handleStats,
43
+ '--help': handleHelp,
44
+ '-h': handleHelp,
45
+ };
46
+
47
+ /**
48
+ * Parse CLI arguments into structured args object.
49
+ * @param {string[]} argv - Raw CLI arguments
50
+ * @returns {Object} Parsed args
51
+ */
52
+ function parseArgs(argv) {
53
+ const args = {
54
+ command: null,
55
+ format: 'ascii',
56
+ file: null,
57
+ interval: 5,
58
+ watch: false,
59
+ help: false,
60
+ };
61
+
62
+ for (let i = 0; i < argv.length; i++) {
63
+ const arg = argv[i];
64
+
65
+ if (arg === '--help' || arg === '-h') {
66
+ args.help = true;
67
+ args.command = '--help';
68
+ } else if (arg === '--deps') {
69
+ args.command = '--deps';
70
+ } else if (arg === '--stats') {
71
+ args.command = '--stats';
72
+ } else if (arg === '--watch') {
73
+ args.watch = true;
74
+ } else if (arg === '--format' && i + 1 < argv.length) {
75
+ args.format = argv[++i];
76
+ } else if (arg.startsWith('--format=')) {
77
+ args.format = arg.split('=')[1];
78
+ } else if (arg === '--interval' && i + 1 < argv.length) {
79
+ args.interval = parseInt(argv[++i], 10);
80
+ } else if (arg.startsWith('--interval=')) {
81
+ args.interval = parseInt(arg.split('=')[1], 10);
82
+ } else if (arg.startsWith('--') && !args.command) {
83
+ args.command = arg;
84
+ }
85
+ }
86
+
87
+ return args;
88
+ }
89
+
90
+ /**
91
+ * Handle --deps command: render dependency tree or formatted output.
92
+ * If --watch is set, delegates to handleWatch.
93
+ * @param {Object} args - Parsed CLI args
94
+ */
95
+ async function handleDeps(args) {
96
+ const format = args.format || 'ascii';
97
+
98
+ if (format !== 'ascii' && !FORMAT_MAP[format]) {
99
+ console.error(`Unknown format: ${format}. Valid formats: ${VALID_FORMATS.join(', ')}`);
100
+ process.exit(1);
101
+ }
102
+
103
+ if (args.watch) {
104
+ return handleWatch(args);
105
+ }
106
+
107
+ const source = new CodeIntelSource();
108
+ const graphData = await source.getData();
109
+
110
+ if (format === 'html') {
111
+ return handleHtmlOutput(graphData);
112
+ }
113
+
114
+ if (format !== 'ascii') {
115
+ const formatter = FORMAT_MAP[format];
116
+ process.stdout.write(formatter(graphData) + '\n');
117
+ return;
118
+ }
119
+
120
+ const isTTY = process.stdout.isTTY;
121
+ const output = renderTree(graphData, { color: isTTY, unicode: isTTY });
122
+ console.log(output);
123
+ }
124
+
125
+ /**
126
+ * Write HTML graph to .aios/graph.html and open in default browser.
127
+ * @param {Object} graphData - Normalized graph data
128
+ * @param {Object} [options] - Options passed to formatAsHtml
129
+ * @returns {string} Output file path
130
+ */
131
+ function handleHtmlOutput(graphData, options = {}) {
132
+ const outputDir = path.resolve(process.cwd(), '.aios');
133
+ const outputPath = path.join(outputDir, 'graph.html');
134
+
135
+ fs.mkdirSync(outputDir, { recursive: true });
136
+
137
+ const html = formatAsHtml(graphData, options);
138
+ fs.writeFileSync(outputPath, html, 'utf8');
139
+
140
+ const nodeCount = (graphData.nodes || []).length;
141
+ console.log(`HTML graph written to ${outputPath} (${nodeCount} entities)`);
142
+
143
+ openInBrowser(outputPath);
144
+ return outputPath;
145
+ }
146
+
147
+ /**
148
+ * Open a file in the default browser (cross-platform).
149
+ * @param {string} filePath - Absolute path to file
150
+ */
151
+ function openInBrowser(filePath) {
152
+ const platform = process.platform;
153
+ const cmd = platform === 'win32' ? 'start ""' : platform === 'darwin' ? 'open' : 'xdg-open';
154
+
155
+ exec(`${cmd} "${filePath}"`, (err) => {
156
+ if (err) {
157
+ console.log(`Could not open browser automatically. Open manually: ${filePath}`);
158
+ }
159
+ });
160
+ }
161
+
162
+ /**
163
+ * Handle --watch mode: regenerate graph file on interval and on file changes.
164
+ * Writes to .aios/graph.dot, .aios/graph.mmd, or .aios/graph.html.
165
+ * @param {Object} args - Parsed CLI args
166
+ * @returns {Object} Watch state for cleanup (used by tests)
167
+ */
168
+ async function handleWatch(args) {
169
+ const watchFormat = WATCH_FORMAT_MAP[args.format] ? args.format : 'dot';
170
+ const { formatter, filename } = WATCH_FORMAT_MAP[watchFormat];
171
+ const intervalMs = (args.interval || 5) * 1000;
172
+ const outputDir = path.resolve(process.cwd(), '.aios');
173
+ const outputPath = path.join(outputDir, filename);
174
+
175
+ fs.mkdirSync(outputDir, { recursive: true });
176
+
177
+ const source = new CodeIntelSource();
178
+
179
+ async function regenerate() {
180
+ try {
181
+ const graphData = await source.getData();
182
+ const content = formatter(graphData);
183
+ fs.writeFileSync(outputPath, content, 'utf8');
184
+ const nodeCount = (graphData.nodes || []).length;
185
+ console.log(`[watch] ${filename} updated (${nodeCount} entities)`);
186
+ } catch (err) {
187
+ console.error(`[watch] regeneration failed: ${err.message}`);
188
+ }
189
+ }
190
+
191
+ await regenerate();
192
+
193
+ const intervalId = setInterval(regenerate, intervalMs);
194
+
195
+ let fileWatcher = null;
196
+ let debounceTimer = null;
197
+ const registryPath = path.resolve(process.cwd(), '.aios-core/data/entity-registry.yaml');
198
+
199
+ try {
200
+ if (fs.existsSync(registryPath)) {
201
+ fileWatcher = fs.watch(registryPath, () => {
202
+ if (debounceTimer) {
203
+ clearTimeout(debounceTimer);
204
+ }
205
+ debounceTimer = setTimeout(regenerate, DEBOUNCE_MS);
206
+ });
207
+ }
208
+ } catch (_err) {
209
+ // fs.watch not available or path inaccessible; interval-only mode
210
+ }
211
+
212
+ function cleanup() {
213
+ clearInterval(intervalId);
214
+ if (debounceTimer) {
215
+ clearTimeout(debounceTimer);
216
+ }
217
+ if (fileWatcher) {
218
+ fileWatcher.close();
219
+ }
220
+ console.log('[watch] stopped');
221
+ }
222
+
223
+ process.once('SIGINT', () => {
224
+ cleanup();
225
+ process.exit(0);
226
+ });
227
+
228
+ return { intervalId, fileWatcher, cleanup, outputPath };
229
+ }
230
+
231
+ /**
232
+ * Handle --stats command: render entity statistics and cache metrics.
233
+ * @param {Object} args - Parsed CLI args
234
+ */
235
+ async function handleStats(_args) {
236
+ const registrySource = new RegistrySource();
237
+ const metricsSource = new MetricsSource();
238
+ const [registryData, metricsData] = await Promise.all([
239
+ registrySource.getData(),
240
+ metricsSource.getData(),
241
+ ]);
242
+ const isTTY = process.stdout.isTTY;
243
+ const output = renderStats(registryData, metricsData, { isTTY: !!isTTY });
244
+
245
+ process.stdout.write(output + '\n');
246
+ }
247
+
248
+ /**
249
+ * Handle --help command: show usage text.
250
+ */
251
+ function handleHelp() {
252
+ const usage = `
253
+ Usage: aios graph [command] [options]
254
+
255
+ Commands:
256
+ --deps Show dependency tree as ASCII text
257
+ --stats Show entity statistics and cache metrics
258
+ --help, -h Show this help message
259
+
260
+ Options:
261
+ --format=FORMAT Output format: ascii (default), json, dot, mermaid, html
262
+ --watch Live mode: regenerate graph file on interval
263
+ --interval=N Seconds between regeneration in watch mode (default: 5)
264
+
265
+ Examples:
266
+ aios graph --deps Show dependency tree
267
+ aios graph --deps --format=json Output as JSON
268
+ aios graph --deps --format=html Interactive HTML graph (opens browser)
269
+ aios graph --deps --watch Live DOT file for VS Code preview
270
+ aios graph --deps --watch --format=html Live HTML with auto-refresh
271
+ aios graph --deps --watch --format=mermaid Live Mermaid file
272
+ aios graph --deps --watch --interval=10 Refresh every 10 seconds
273
+ aios graph --stats Show entity stats and cache metrics
274
+ `.trim();
275
+
276
+ console.log(usage);
277
+ }
278
+
279
+ /**
280
+ * Handle default summary view: dependency tree (compact) + stats + provider status.
281
+ * @param {Object} args - Parsed CLI args
282
+ */
283
+ async function handleSummary(args) {
284
+ const codeIntelSource = new CodeIntelSource();
285
+ const registrySource = new RegistrySource();
286
+ const metricsSource = new MetricsSource();
287
+
288
+ const [graphData, registryData, metricsData] = await Promise.all([
289
+ codeIntelSource.getData(),
290
+ registrySource.getData(),
291
+ metricsSource.getData(),
292
+ ]);
293
+
294
+ const isTTY = !!process.stdout.isTTY;
295
+ const sections = [];
296
+
297
+ sections.push('AIOS Graph Dashboard');
298
+ sections.push(isTTY ? '\u2550'.repeat(35) : '='.repeat(35));
299
+ sections.push('');
300
+
301
+ const treeOutput = renderTree(graphData, {
302
+ color: isTTY,
303
+ unicode: isTTY,
304
+ maxPerCategory: MAX_SUMMARY_PER_CATEGORY,
305
+ });
306
+ sections.push(treeOutput);
307
+ sections.push('');
308
+
309
+ const statsOutput = renderStats(registryData, metricsData, { isTTY });
310
+ sections.push(statsOutput);
311
+ sections.push('');
312
+
313
+ const statusOutput = renderStatus(metricsData, { isTTY });
314
+ sections.push(statusOutput);
315
+
316
+ process.stdout.write(sections.join('\n') + '\n');
317
+ }
318
+
319
+ /**
320
+ * Main CLI entry point.
321
+ * @param {string[]} argv - Raw CLI arguments
322
+ */
323
+ async function run(argv) {
324
+ const args = parseArgs(argv);
325
+
326
+ if (args.help) {
327
+ handleHelp();
328
+ return;
329
+ }
330
+
331
+ if (args.command === null) {
332
+ return handleSummary(args);
333
+ }
334
+
335
+ const handler = COMMANDS[args.command];
336
+ if (!handler) {
337
+ console.error(`Unknown command: ${args.command}`);
338
+ handleHelp();
339
+ process.exit(1);
340
+ }
341
+
342
+ return handler(args);
343
+ }
344
+
345
+ module.exports = {
346
+ run,
347
+ parseArgs,
348
+ handleDeps,
349
+ handleStats,
350
+ handleHelp,
351
+ handleWatch,
352
+ handleSummary,
353
+ handleHtmlOutput,
354
+ openInBrowser,
355
+ MAX_SUMMARY_PER_CATEGORY,
356
+ DEFAULT_WATCH_INTERVAL_MS,
357
+ DEBOUNCE_MS,
358
+ FORMAT_MAP,
359
+ VALID_FORMATS,
360
+ WATCH_FORMAT_MAP,
361
+ };
@@ -0,0 +1,234 @@
1
+ 'use strict';
2
+
3
+ const { getClient, isCodeIntelAvailable } = require('../../code-intel');
4
+ const { RegistryLoader } = require('../../ids/registry-loader');
5
+
6
+ /**
7
+ * Data source that provides normalized graph data from code-intel
8
+ * or falls back to entity-registry.yaml when provider is offline.
9
+ */
10
+ /**
11
+ * Classify a script entity into subcategory based on its path.
12
+ * @param {string} filePath - Entity path
13
+ * @returns {string} 'scripts/task' | 'scripts/engine' | 'scripts/infra'
14
+ */
15
+ function _classifyScript(filePath) {
16
+ if (filePath.includes('/development/scripts/')) return 'scripts/task';
17
+ if (filePath.includes('/core/')) return 'scripts/engine';
18
+ if (filePath.includes('/infrastructure/')) return 'scripts/infra';
19
+ return 'scripts/task';
20
+ }
21
+
22
+ /**
23
+ * Detect fine-grained category from entity path and base category.
24
+ * @param {string} baseCategory - Original category from registry/provider
25
+ * @param {string} filePath - Entity path
26
+ * @returns {string} Refined category
27
+ */
28
+ function _detectCategory(baseCategory, filePath) {
29
+ const path = (filePath || '').toLowerCase();
30
+
31
+ if (path.includes('/checklists/')) return 'checklists';
32
+ if (path.includes('/workflows/')) return 'workflows';
33
+ if (path.includes('/utils/')) return 'utils';
34
+ if (path.includes('/data/')) return 'data';
35
+ if (path.includes('/tools/')) return 'tools';
36
+
37
+ if (baseCategory === 'scripts' || path.includes('/scripts/')) {
38
+ return _classifyScript(path);
39
+ }
40
+
41
+ return baseCategory;
42
+ }
43
+
44
+ class CodeIntelSource {
45
+ /**
46
+ * @param {Object} [options]
47
+ * @param {number} [options.cacheTTL=5000] - Cache TTL in milliseconds
48
+ */
49
+ constructor(options = {}) {
50
+ this._cache = null;
51
+ this._cacheTimestamp = 0;
52
+ this._cacheTTL = options.cacheTTL || 5000;
53
+ }
54
+
55
+ /**
56
+ * Get normalized graph data.
57
+ * Primary: code-intel provider (live data).
58
+ * Fallback: entity-registry.yaml (static data).
59
+ * @returns {Promise<Object>} { nodes, edges, source, isFallback, timestamp }
60
+ */
61
+ async getData() {
62
+ if (this._cache && !this.isStale()) {
63
+ return this._cache;
64
+ }
65
+
66
+ let result;
67
+
68
+ try {
69
+ if (isCodeIntelAvailable()) {
70
+ const client = getClient();
71
+ const deps = await client.analyzeDependencies('.');
72
+ result = this._wrap(this._normalizeDeps(deps), 'code-intel', false);
73
+ } else {
74
+ result = this._getRegistryFallback();
75
+ }
76
+ } catch (_err) {
77
+ result = this._getRegistryFallback();
78
+ }
79
+
80
+ this._cache = result;
81
+ this._cacheTimestamp = Date.now();
82
+ return result;
83
+ }
84
+
85
+ /**
86
+ * Get timestamp of last successful data fetch.
87
+ * @returns {number}
88
+ */
89
+ getLastUpdate() {
90
+ return this._cacheTimestamp;
91
+ }
92
+
93
+ /**
94
+ * Check if cached data is expired.
95
+ * @returns {boolean}
96
+ */
97
+ isStale() {
98
+ return Date.now() - this._cacheTimestamp > this._cacheTTL;
99
+ }
100
+
101
+ /**
102
+ * Load graph data from entity-registry.yaml as fallback.
103
+ * @returns {Object} Wrapped graph data
104
+ */
105
+ _getRegistryFallback() {
106
+ try {
107
+ const loader = new RegistryLoader();
108
+ const registry = loader.load();
109
+ return this._wrap(this._registryToTree(registry), 'registry', true);
110
+ } catch (_err) {
111
+ return this._wrap({ nodes: [], edges: [] }, 'registry', true);
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Normalize analyzeDependencies output to { nodes, edges }.
117
+ * Handles various shapes from different providers.
118
+ * @param {*} deps - Raw output from analyzeDependencies
119
+ * @returns {Object} { nodes: Array, edges: Array }
120
+ */
121
+ _normalizeDeps(deps) {
122
+ if (!deps) {
123
+ return { nodes: [], edges: [] };
124
+ }
125
+
126
+ // Already normalized format
127
+ if (Array.isArray(deps.nodes) && Array.isArray(deps.edges)) {
128
+ return { nodes: deps.nodes, edges: deps.edges };
129
+ }
130
+
131
+ // Array of dependency objects
132
+ if (Array.isArray(deps)) {
133
+ const nodes = [];
134
+ const edges = [];
135
+ const seen = new Set();
136
+
137
+ for (const dep of deps) {
138
+ const id = dep.id || dep.name || dep.path;
139
+ if (!id || seen.has(id)) continue;
140
+ seen.add(id);
141
+
142
+ nodes.push({
143
+ id,
144
+ label: dep.label || dep.name || id,
145
+ type: dep.type || 'unknown',
146
+ path: dep.path || '',
147
+ category: _detectCategory(dep.category || dep.type || 'other', dep.path || ''),
148
+ });
149
+
150
+ for (const target of dep.dependencies || dep.deps || []) {
151
+ edges.push({ from: id, to: target, type: 'depends' });
152
+ }
153
+ }
154
+
155
+ return { nodes, edges };
156
+ }
157
+
158
+ // Flat object with dependencies property
159
+ if (deps.dependencies && typeof deps.dependencies === 'object') {
160
+ return this._normalizeDeps(
161
+ Object.entries(deps.dependencies).map(([key, val]) => ({
162
+ id: key,
163
+ ...((typeof val === 'object' && val) || {}),
164
+ }))
165
+ );
166
+ }
167
+
168
+ return { nodes: [], edges: [] };
169
+ }
170
+
171
+ /**
172
+ * Convert entity-registry to normalized graph format.
173
+ * Groups entities by category and maps dependencies/usedBy to edges.
174
+ * @param {Object} registry - Loaded registry data
175
+ * @returns {Object} { nodes: Array, edges: Array }
176
+ */
177
+ _registryToTree(registry) {
178
+ const nodes = [];
179
+ const edges = [];
180
+ const edgeSet = new Set();
181
+
182
+ for (const [category, entities] of Object.entries(registry.entities || {})) {
183
+ if (!entities || typeof entities !== 'object') continue;
184
+
185
+ for (const [entityId, entity] of Object.entries(entities)) {
186
+ nodes.push({
187
+ id: entityId,
188
+ label: entityId,
189
+ type: entity.type || category,
190
+ path: entity.path || '',
191
+ category: _detectCategory(category, entity.path || ''),
192
+ lifecycle: entity.lifecycle || 'production', // NOG-16C: pass lifecycle for graph filtering
193
+ });
194
+
195
+ for (const dep of entity.dependencies || []) {
196
+ const edgeKey = `${entityId}->depends->${dep}`;
197
+ if (!edgeSet.has(edgeKey)) {
198
+ edgeSet.add(edgeKey);
199
+ edges.push({ from: entityId, to: dep, type: 'depends' });
200
+ }
201
+ }
202
+
203
+ for (const consumer of entity.usedBy || []) {
204
+ const edgeKey = `${consumer}->uses->${entityId}`;
205
+ if (!edgeSet.has(edgeKey)) {
206
+ edgeSet.add(edgeKey);
207
+ edges.push({ from: consumer, to: entityId, type: 'uses' });
208
+ }
209
+ }
210
+ }
211
+ }
212
+
213
+ return { nodes, edges };
214
+ }
215
+
216
+ /**
217
+ * Wrap data in standard result envelope.
218
+ * @param {Object} data - { nodes, edges }
219
+ * @param {string} source - 'code-intel' | 'registry'
220
+ * @param {boolean} isFallback
221
+ * @returns {Object}
222
+ */
223
+ _wrap(data, source, isFallback) {
224
+ return {
225
+ nodes: data.nodes || [],
226
+ edges: data.edges || [],
227
+ source,
228
+ isFallback,
229
+ timestamp: Date.now(),
230
+ };
231
+ }
232
+ }
233
+
234
+ module.exports = { CodeIntelSource, _classifyScript, _detectCategory };
@@ -0,0 +1,95 @@
1
+ 'use strict';
2
+
3
+ const { getClient, isCodeIntelAvailable } = require('../../code-intel');
4
+
5
+ /**
6
+ * Data source that provides cache and latency metrics from the code-intel client.
7
+ * Falls back to offline data when Code Graph MCP is unavailable.
8
+ * Implements the same interface as CodeIntelSource: getData(), getLastUpdate(), isStale().
9
+ */
10
+ class MetricsSource {
11
+ /**
12
+ * @param {Object} [options]
13
+ * @param {number} [options.cacheTTL=5000] - Cache TTL in milliseconds
14
+ */
15
+ constructor(options = {}) {
16
+ this._cache = null;
17
+ this._cacheTimestamp = 0;
18
+ this._cacheTTL = options.cacheTTL || 5000;
19
+ }
20
+
21
+ /**
22
+ * Get metrics from code-intel client.
23
+ * Primary: live metrics from active provider.
24
+ * Fallback: offline placeholder with providerAvailable=false.
25
+ * @returns {Promise<Object>} Metrics object
26
+ */
27
+ async getData() {
28
+ if (this._cache && !this.isStale()) {
29
+ return this._cache;
30
+ }
31
+
32
+ let result;
33
+
34
+ try {
35
+ if (isCodeIntelAvailable()) {
36
+ const client = getClient();
37
+ const metrics = client.getMetrics();
38
+ result = {
39
+ cacheHits: metrics.cacheHits || 0,
40
+ cacheMisses: metrics.cacheMisses || 0,
41
+ cacheHitRate: metrics.cacheHitRate || 0,
42
+ circuitBreakerState: metrics.circuitBreakerState || 'CLOSED',
43
+ latencyLog: metrics.latencyLog || [],
44
+ providerAvailable: true,
45
+ activeProvider: metrics.activeProvider || null,
46
+ timestamp: Date.now(),
47
+ };
48
+ } else {
49
+ result = this._offlineMetrics();
50
+ }
51
+ } catch (_err) {
52
+ result = this._offlineMetrics();
53
+ }
54
+
55
+ this._cache = result;
56
+ this._cacheTimestamp = Date.now();
57
+ return result;
58
+ }
59
+
60
+ /**
61
+ * Get timestamp of last successful data fetch.
62
+ * @returns {number}
63
+ */
64
+ getLastUpdate() {
65
+ return this._cacheTimestamp;
66
+ }
67
+
68
+ /**
69
+ * Check if cached data is expired.
70
+ * @returns {boolean}
71
+ */
72
+ isStale() {
73
+ return Date.now() - this._cacheTimestamp > this._cacheTTL;
74
+ }
75
+
76
+ /**
77
+ * Return offline placeholder metrics.
78
+ * @returns {Object}
79
+ * @private
80
+ */
81
+ _offlineMetrics() {
82
+ return {
83
+ cacheHits: 0,
84
+ cacheMisses: 0,
85
+ cacheHitRate: 0,
86
+ circuitBreakerState: 'CLOSED',
87
+ latencyLog: [],
88
+ providerAvailable: false,
89
+ activeProvider: null,
90
+ timestamp: Date.now(),
91
+ };
92
+ }
93
+ }
94
+
95
+ module.exports = { MetricsSource };