titan-agent 5.4.2 → 5.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/agent/agent.js +9 -5
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/agentLoop.js +7 -3
- package/dist/agent/agentLoop.js.map +1 -1
- package/dist/agent/checkpoint.js +2 -2
- package/dist/agent/checkpoint.js.map +1 -1
- package/dist/agent/commandPost.js +3 -3
- package/dist/agent/commandPost.js.map +1 -1
- package/dist/agent/goalProposer.js +2 -2
- package/dist/agent/goalProposer.js.map +1 -1
- package/dist/agent/goals.js +3 -3
- package/dist/agent/goals.js.map +1 -1
- package/dist/agent/peerAdvise.js +1 -1
- package/dist/agent/peerAdvise.js.map +1 -1
- package/dist/agent/planner.js +4 -4
- package/dist/agent/planner.js.map +1 -1
- package/dist/agent/userProfile.js +2 -2
- package/dist/agent/userProfile.js.map +1 -1
- package/dist/cli/doctor.js +33 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/onboard.js +4 -4
- package/dist/cli/onboard.js.map +1 -1
- package/dist/config/config.js +3 -3
- package/dist/config/config.js.map +1 -1
- package/dist/config/schema.js +8 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/gateway/routes/adminRouter.js +500 -0
- package/dist/gateway/routes/adminRouter.js.map +1 -0
- package/dist/gateway/routes/agents.js +231 -0
- package/dist/gateway/routes/agents.js.map +1 -0
- package/dist/gateway/routes/agentsRouter.js +32 -0
- package/dist/gateway/routes/agentsRouter.js.map +1 -0
- package/dist/gateway/routes/checkpoints.js +41 -0
- package/dist/gateway/routes/checkpoints.js.map +1 -0
- package/dist/gateway/routes/commandPost.js +755 -0
- package/dist/gateway/routes/commandPost.js.map +1 -0
- package/dist/gateway/routes/companies.js +166 -0
- package/dist/gateway/routes/companies.js.map +1 -0
- package/dist/gateway/routes/files.js +295 -0
- package/dist/gateway/routes/files.js.map +1 -0
- package/dist/gateway/routes/hardwareRouter.js +151 -0
- package/dist/gateway/routes/hardwareRouter.js.map +1 -0
- package/dist/gateway/routes/mcpRouter.js +88 -0
- package/dist/gateway/routes/mcpRouter.js.map +1 -0
- package/dist/gateway/routes/mesh.js +464 -0
- package/dist/gateway/routes/mesh.js.map +1 -0
- package/dist/gateway/routes/metricsRouter.js +131 -0
- package/dist/gateway/routes/metricsRouter.js.map +1 -0
- package/dist/gateway/routes/organism.js +82 -0
- package/dist/gateway/routes/organism.js.map +1 -0
- package/dist/gateway/routes/paperclip.js +101 -0
- package/dist/gateway/routes/paperclip.js.map +1 -0
- package/dist/gateway/routes/sessions.js +227 -0
- package/dist/gateway/routes/sessions.js.map +1 -0
- package/dist/gateway/routes/skills.js +295 -0
- package/dist/gateway/routes/skills.js.map +1 -0
- package/dist/gateway/routes/socialRouter.js +145 -0
- package/dist/gateway/routes/socialRouter.js.map +1 -0
- package/dist/gateway/routes/systemRouter.js +220 -0
- package/dist/gateway/routes/systemRouter.js.map +1 -0
- package/dist/gateway/routes/teamsRecipes.js +297 -0
- package/dist/gateway/routes/teamsRecipes.js.map +1 -0
- package/dist/gateway/routes/tests.js +401 -0
- package/dist/gateway/routes/tests.js.map +1 -0
- package/dist/gateway/routes/traces.js +33 -0
- package/dist/gateway/routes/traces.js.map +1 -0
- package/dist/gateway/routes/voiceRouter.js +770 -0
- package/dist/gateway/routes/voiceRouter.js.map +1 -0
- package/dist/gateway/routes/watchRouter.js +131 -0
- package/dist/gateway/routes/watchRouter.js.map +1 -0
- package/dist/gateway/server.js +1179 -7379
- package/dist/gateway/server.js.map +1 -1
- package/dist/mcp/registry.js +2 -2
- package/dist/mcp/registry.js.map +1 -1
- package/dist/memory/episodic.js +2 -2
- package/dist/memory/episodic.js.map +1 -1
- package/dist/memory/learning.js +3 -3
- package/dist/memory/learning.js.map +1 -1
- package/dist/memory/memory.js +3 -3
- package/dist/memory/memory.js.map +1 -1
- package/dist/organism/drives.js +2 -2
- package/dist/organism/drives.js.map +1 -1
- package/dist/providers/errorTaxonomy.js +13 -0
- package/dist/providers/errorTaxonomy.js.map +1 -1
- package/dist/providers/ollama.js +3 -1
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai_compat.js +4 -3
- package/dist/providers/openai_compat.js.map +1 -1
- package/dist/providers/router.js +13 -0
- package/dist/providers/router.js.map +1 -1
- package/dist/safety/fixOscillation.js +15 -0
- package/dist/safety/fixOscillation.js.map +1 -1
- package/dist/safety/killSwitch.js +2 -2
- package/dist/safety/killSwitch.js.map +1 -1
- package/dist/safety/selfRepair.js +7 -3
- package/dist/safety/selfRepair.js.map +1 -1
- package/dist/skills/builtin/agent_debate.js +2 -2
- package/dist/skills/builtin/agent_debate.js.map +1 -1
- package/dist/skills/builtin/apply_patch.js +3 -3
- package/dist/skills/builtin/apply_patch.js.map +1 -1
- package/dist/skills/builtin/shell.js +2 -2
- package/dist/skills/builtin/shell.js.map +1 -1
- package/dist/skills/builtin/voice_control.js +49 -0
- package/dist/skills/builtin/voice_control.js.map +1 -0
- package/dist/skills/builtin/widget_gallery.js +6 -1
- package/dist/skills/builtin/widget_gallery.js.map +1 -1
- package/dist/skills/registry.js +15 -4
- package/dist/skills/registry.js.map +1 -1
- package/dist/storage/JsonStorage.js +4 -4
- package/dist/storage/JsonStorage.js.map +1 -1
- package/dist/utils/constants.js +1 -1
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/helpers.js +3 -1
- package/dist/utils/helpers.js.map +1 -1
- package/dist/utils/lifecycle.js +86 -0
- package/dist/utils/lifecycle.js.map +1 -0
- package/dist/voice/bridge.js +136 -0
- package/dist/voice/bridge.js.map +1 -0
- package/docs/COO-MASTER-PLAN-2026-05-02.md +474 -0
- package/docs/HANDOFF/2026-04-29.md +141 -0
- package/docs/HANDOFF-2026-04-30.md +144 -0
- package/docs/HANDOFF-2026-05-03.md +114 -0
- package/docs/adr/2026-04-29-widget-pipeline-traceability.md +49 -0
- package/docs/agent-memory/README.md +45 -0
- package/docs/agent-memory/commands.md +100 -0
- package/docs/agent-memory/context-tree.md +101 -0
- package/docs/agent-memory/current-state.md +54 -0
- package/docs/agent-memory/decisions.md +78 -0
- package/docs/agent-memory/known-issues.md +76 -0
- package/docs/agent-memory/reflections.md +52 -0
- package/docs/agent-memory/skills-candidates.md +80 -0
- package/docs/superpowers/plans/2026-04-29-comprehensive-audit.md +256 -0
- package/docs/superpowers/plans/2026-04-29-comprehensive-test-plan.md +396 -0
- package/docs/superpowers/plans/2026-04-29-fix-all-prs.md +251 -0
- package/docs/superpowers/plans/2026-04-29-gitnexus-gap-remediation.md +969 -0
- package/package.json +5 -2
- package/ui/dist/assets/{AuditPanel-CM6Wg9hO.js → AuditPanel-VzSndmDN.js} +2 -2
- package/ui/dist/assets/{AutonomyPanel-CESx3ANg.js → AutonomyPanel-BiFouzAV.js} +2 -2
- package/ui/dist/assets/AutopilotPanel-fjOfM668.js +1 -0
- package/ui/dist/assets/{AutoresearchPanel-DR47NqT5.js → AutoresearchPanel-CVCxzAH3.js} +2 -2
- package/ui/dist/assets/BackupPanel-CHVTG--q.js +1 -0
- package/ui/dist/assets/{BrowserPanel-C15x9bLn.js → BrowserPanel-D5mvMKFU.js} +2 -2
- package/ui/dist/assets/CPActivity-B12mt35m.js +1 -0
- package/ui/dist/assets/CPAgentDetail-DsdShc-1.js +1 -0
- package/ui/dist/assets/CPAgents-j_7C-oQV.js +1 -0
- package/ui/dist/assets/CPApprovals-BShKSX9X.js +1 -0
- package/ui/dist/assets/CPCosts-CKPlhBDs.js +1 -0
- package/ui/dist/assets/CPDashboard-11c0nkxK.js +1 -0
- package/ui/dist/assets/CPFiles-BhLEOnXy.js +1 -0
- package/ui/dist/assets/CPGoals-Bi3t1b2P.js +1 -0
- package/ui/dist/assets/CPInbox-Bbr7khp6.js +11 -0
- package/ui/dist/assets/CPIssueDetail-DSdgNK8r.js +1 -0
- package/ui/dist/assets/CPIssues-DDEVKhX6.js +1 -0
- package/ui/dist/assets/CPLayout-DgPOfyGv.js +17 -0
- package/ui/dist/assets/CPOrg-Df73RrRJ.js +8 -0
- package/ui/dist/assets/CPRuns-ByioAz8w.js +1 -0
- package/ui/dist/assets/{CPSocial-nb-j7sOE.js → CPSocial-Dlnr_w1X.js} +2 -2
- package/ui/dist/assets/ChannelsPanel-DQjQCTK5.js +1 -0
- package/ui/dist/assets/CheckpointsPanel-C4vKjlAJ.js +1 -0
- package/ui/dist/assets/CommandPostHub-C9pp5Giq.js +24 -0
- package/ui/dist/assets/CronPanel-C6bzUfrD.js +1 -0
- package/ui/dist/assets/DaemonPanel-BA5Tb_UO.js +1 -0
- package/ui/dist/assets/{DataTable-B2Ma8hfi.js → DataTable-CH7IYJJh.js} +1 -1
- package/ui/dist/assets/{EmptyState-CcKyk5Yn.js → EmptyState-jU6yNDnF.js} +1 -1
- package/ui/dist/assets/{EvalHarnessPanel-BqtMc1ZM.js → EvalHarnessPanel-DnYqredY.js} +2 -2
- package/ui/dist/assets/EvalPanel-ChO7CD1r.js +1 -0
- package/ui/dist/assets/{FilesPanel-3QKvrWPo.js → FilesPanel-CaUkv2is.js} +2 -2
- package/ui/dist/assets/FleetPanel-DC_5uj0N.js +1 -0
- package/ui/dist/assets/{HomelabPanel-DhrjTX9m.js → HomelabPanel-CE5PGRpL.js} +2 -2
- package/ui/dist/assets/InfraView-C-uSlvb9.js +2 -0
- package/ui/dist/assets/InlineEditableField-BMQjiE6-.js +1 -0
- package/ui/dist/assets/Input-Bu_b3qmY.js +1 -0
- package/ui/dist/assets/IntegrationsPanel-DsYpAq43.js +1 -0
- package/ui/dist/assets/IntelligenceView-DUdIO1K7.js +2 -0
- package/ui/dist/assets/LearningPanel-UpQZC-mA.js +1 -0
- package/ui/dist/assets/LogsPanel-ClXJ4fcr.js +1 -0
- package/ui/dist/assets/McpPanel-JKgtIERQ.js +1 -0
- package/ui/dist/assets/{MemoryGraphPanel-Bzvjmzvk.js → MemoryGraphPanel-Bo2OrvA6.js} +2 -2
- package/ui/dist/assets/MemoryWikiPanel-BqJ1AmYm.js +11 -0
- package/ui/dist/assets/{MeshPanel-C3LJSlht.js → MeshPanel-BJVGYvwk.js} +2 -2
- package/ui/dist/assets/Modal-CAAooiZU.js +1 -0
- package/ui/dist/assets/NvidiaPanel-BtCg3G4w.js +1 -0
- package/ui/dist/assets/OrganismPanel-DgrTTzcF.js +1 -0
- package/ui/dist/assets/OverviewPanel-rVav1Hox.js +1 -0
- package/ui/dist/assets/{PageHeader-BimceqQo.js → PageHeader-CnZtP8ek.js} +1 -1
- package/ui/dist/assets/PaperclipPanel-C-FKdhiF.js +1 -0
- package/ui/dist/assets/{PersonasPanel-L1j78p6H.js → PersonasPanel-BmlxokfB.js} +1 -1
- package/ui/dist/assets/RecipesPanel-BNKKChis.js +1 -0
- package/ui/dist/assets/SecurityPanel-I7JRHiNy.js +1 -0
- package/ui/dist/assets/SelfImprovePanel-u9h0Lt3p.js +1 -0
- package/ui/dist/assets/{SelfProposalsPanel-lNmiDThB.js → SelfProposalsPanel-DKl9iBjM.js} +2 -2
- package/ui/dist/assets/SessionsPanel-BhRiWI_g.js +1 -0
- package/ui/dist/assets/{SessionsTab-JQbltWww.js → SessionsTab-Bk08wyeY.js} +1 -1
- package/ui/dist/assets/SettingsPanel-haLfmG2k.js +1 -0
- package/ui/dist/assets/SettingsView--gi3fxI8.js +2 -0
- package/ui/dist/assets/{SkeletonLoader-atQtpcF5.js → SkeletonLoader-B5v09EF_.js} +1 -1
- package/ui/dist/assets/{SkillsPanel-DlFs2ih7.js → SkillsPanel-BlAHFLcQ.js} +1 -1
- package/ui/dist/assets/SomaView-CExtS3zw.js +5 -0
- package/ui/dist/assets/{StatCard-DciE_Iqc.js → StatCard-BIsyMybM.js} +1 -1
- package/ui/dist/assets/{StatusBadge-BtfSPoW2.js → StatusBadge-D5nU7El8.js} +1 -1
- package/ui/dist/assets/Tabs-BBYZrBI8.js +1 -0
- package/ui/dist/assets/TeamsPanel-LPXJg823.js +1 -0
- package/ui/dist/assets/TelemetryPanel-EqpRBmOI.js +1 -0
- package/ui/dist/assets/TitanCanvas-BCbWnLMd.js +985 -0
- package/ui/dist/assets/ToolsView-CeP0Zz-N.js +2 -0
- package/ui/dist/assets/{Tooltip-70UK0E2I.js → Tooltip-BSO2XVpF.js} +1 -1
- package/ui/dist/assets/TraceViewer-BKI7o5B0.js +1 -0
- package/ui/dist/assets/TrainingPanel-c-RhjdE1.js +1 -0
- package/ui/dist/assets/VoiceOverlay-D-gc58b0.js +27 -0
- package/ui/dist/assets/VramPanel-C6xc7zgd.js +1 -0
- package/ui/dist/assets/{WatchView-C-sGFpVy.js → WatchView-dqBVCVH0.js} +1 -1
- package/ui/dist/assets/WorkTab-CBoLNrTM.js +1 -0
- package/ui/dist/assets/{WorkflowsPanel-CvgQU1xI.js → WorkflowsPanel-BAnSTOYe.js} +2 -2
- package/ui/dist/assets/approvalHeadline-DB9SgR-9.js +1 -0
- package/ui/dist/assets/{arrow-left-DwqHtJiU.js → arrow-left-5chqas7J.js} +1 -1
- package/ui/dist/assets/briefcase-D4vLzudp.js +6 -0
- package/ui/dist/assets/{chart-column-BtNO6sRy.js → chart-column-CdFlBpoP.js} +1 -1
- package/ui/dist/assets/check-Bpm1IONe.js +6 -0
- package/ui/dist/assets/chevron-down-D7OLjvuD.js +6 -0
- package/ui/dist/assets/chevron-right-aQEw2mUW.js +6 -0
- package/ui/dist/assets/chevron-up-C5g6pEj8.js +6 -0
- package/ui/dist/assets/{circle-check-big-DZRE_MbN.js → circle-check-big-fPhEdP88.js} +1 -1
- package/ui/dist/assets/clock-CTsgP_Sn.js +6 -0
- package/ui/dist/assets/{dollar-sign-aVG3a5eL.js → dollar-sign-CudFVYFc.js} +1 -1
- package/ui/dist/assets/{download-BxiWJU4G.js → download-DZRxDn67.js} +1 -1
- package/ui/dist/assets/external-link-BZ0y_Ahx.js +6 -0
- package/ui/dist/assets/{eye-off-CkgfFYhm.js → eye-off-BmJF0YYx.js} +1 -1
- package/ui/dist/assets/folder-DA43TRCm.js +11 -0
- package/ui/dist/assets/{funnel-PkLdxKyC.js → funnel-J3mULzrz.js} +1 -1
- package/ui/dist/assets/{git-branch-BM-Gw95X.js → git-branch-oHibJqDq.js} +1 -1
- package/ui/dist/assets/{index-D0RJ8701.css → index-BR0vfkIi.css} +1 -1
- package/ui/dist/assets/{index-CahJbWSR.js → index-DzwowwSI.js} +20 -20
- package/ui/dist/assets/{layers-BuGf4FIJ.js → layers-DsyEyu7z.js} +1 -1
- package/ui/dist/assets/{legacy-CR6o4t-y.js → legacy-8ITl64sV.js} +1 -1
- package/ui/dist/assets/{lightbulb-n8gc_XAL.js → lightbulb-C54Ske-p.js} +1 -1
- package/ui/dist/assets/list-todo-Cnd4rdoK.js +6 -0
- package/ui/dist/assets/loader-circle-1YOBsoQp.js +6 -0
- package/ui/dist/assets/network-DbGDAdrn.js +6 -0
- package/ui/dist/assets/{pause-DCV52koX.js → pause-CYhO_uQo.js} +1 -1
- package/ui/dist/assets/{play-CcJ9BnCh.js → play-DVY9c5Ck.js} +1 -1
- package/ui/dist/assets/{plug-CfWBXfCl.js → plug-BcXjlPUL.js} +1 -1
- package/ui/dist/assets/plus-Csu2v9GN.js +6 -0
- package/ui/dist/assets/{proxy-CzZDfLmm.js → proxy-DxS2_9D7.js} +1 -1
- package/ui/dist/assets/rotate-ccw-Co-_W04j.js +6 -0
- package/ui/dist/assets/save-Btx-kpoW.js +6 -0
- package/ui/dist/assets/search-0hXTwEZR.js +6 -0
- package/ui/dist/assets/send-TEpapzQR.js +6 -0
- package/ui/dist/assets/shield-check-DjBJXZUr.js +6 -0
- package/ui/dist/assets/{square-DJpUhlxi.js → square-OweUvjP-.js} +1 -1
- package/ui/dist/assets/{target-DWcmM_9m.js → target-BRW80Xer.js} +1 -1
- package/ui/dist/assets/terminal-BtiqJ628.js +16 -0
- package/ui/dist/assets/{toggle-right-YusFQ69L.js → toggle-right-CKtSrl28.js} +1 -1
- package/ui/dist/assets/{trash-2-CK7yQ55V.js → trash-2-DgWrHVax.js} +1 -1
- package/ui/dist/assets/{trending-up-DGjFyubC.js → trending-up-MpIrE4j6.js} +1 -1
- package/ui/dist/assets/{trophy-uQv_NgDB.js → trophy-CECuZNhX.js} +1 -1
- package/ui/dist/assets/users-dZgv4ePG.js +16 -0
- package/ui/dist/assets/wrench-CDz3xYve.js +11 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/AutopilotPanel-DtEet1hJ.js +0 -1
- package/ui/dist/assets/BackupPanel-BGP8p3l3.js +0 -1
- package/ui/dist/assets/CPAgents-DYUtPzSq.js +0 -1
- package/ui/dist/assets/CPDashboard-Bf0-SyCh.js +0 -6
- package/ui/dist/assets/CPFiles-CxgxjQcO.js +0 -1
- package/ui/dist/assets/CPGoals-BsmCMTvT.js +0 -1
- package/ui/dist/assets/CPInbox-tMSbmQ9H.js +0 -11
- package/ui/dist/assets/ChannelsPanel-DP5C2OKd.js +0 -1
- package/ui/dist/assets/CheckpointsPanel-DlranVLZ.js +0 -1
- package/ui/dist/assets/CommandPostHub-BgxIa4Ev.js +0 -29
- package/ui/dist/assets/CronPanel-LoT5yKwJ.js +0 -1
- package/ui/dist/assets/DaemonPanel-DBGMqaE_.js +0 -1
- package/ui/dist/assets/EvalPanel-Bc33j0pN.js +0 -1
- package/ui/dist/assets/FleetPanel-CSsXuQYj.js +0 -1
- package/ui/dist/assets/InfraView-CR6HyrL6.js +0 -2
- package/ui/dist/assets/InlineEditableField-CnvF-yFR.js +0 -1
- package/ui/dist/assets/Input-GTHp2Rkr.js +0 -1
- package/ui/dist/assets/IntegrationsPanel-CymCRE3T.js +0 -1
- package/ui/dist/assets/IntelligenceView-C1IHxJRC.js +0 -2
- package/ui/dist/assets/LearningPanel-DOCES3lH.js +0 -1
- package/ui/dist/assets/LogsPanel-BLnAqEaZ.js +0 -1
- package/ui/dist/assets/McpPanel-ChUzmr3z.js +0 -1
- package/ui/dist/assets/MemoryWikiPanel-Dwk3Aqwd.js +0 -11
- package/ui/dist/assets/NvidiaPanel-CeZK_-CV.js +0 -1
- package/ui/dist/assets/OrganismPanel-BB6YOiQV.js +0 -1
- package/ui/dist/assets/OverviewPanel-BmtBhQnv.js +0 -1
- package/ui/dist/assets/PaperclipPanel-C-brgwA3.js +0 -1
- package/ui/dist/assets/RecipesPanel-34lCfynJ.js +0 -1
- package/ui/dist/assets/SecurityPanel-CBTPWLj6.js +0 -1
- package/ui/dist/assets/SelfImprovePanel-BrPbFHhG.js +0 -1
- package/ui/dist/assets/SessionsPanel-DAEYIn83.js +0 -1
- package/ui/dist/assets/SettingsPanel-CzRROAYQ.js +0 -1
- package/ui/dist/assets/SettingsView-CN7ii2uw.js +0 -2
- package/ui/dist/assets/SomaView-Ba642Oqb.js +0 -5
- package/ui/dist/assets/TeamsPanel-DKQ5z2Qe.js +0 -1
- package/ui/dist/assets/TelemetryPanel-B6KAc55Q.js +0 -1
- package/ui/dist/assets/TitanCanvas-C-s0A-lv.js +0 -1092
- package/ui/dist/assets/ToolsView-Dei0KMP0.js +0 -2
- package/ui/dist/assets/TraceViewer-BniolyBx.js +0 -1
- package/ui/dist/assets/TrainingPanel-Bz4CTPGW.js +0 -1
- package/ui/dist/assets/VoiceOverlay-CmNCrLcd.js +0 -37
- package/ui/dist/assets/VramPanel-Xh_OtRDR.js +0 -1
- package/ui/dist/assets/WorkTab-BjLNmgIK.js +0 -1
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Router } from "express";
|
|
3
|
+
import { join, dirname } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import { loadavg, cpus } from "os";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import { spawn } from "child_process";
|
|
8
|
+
import { loadConfig } from "../../config/config.js";
|
|
9
|
+
import { TITAN_VERSION, TITAN_WORKSPACE } from "../../utils/constants.js";
|
|
10
|
+
import logger from "../../utils/logger.js";
|
|
11
|
+
import { listEntities, getEntity, getGraphData, getEntityEpisodes } from "../../memory/graph.js";
|
|
12
|
+
const COMPONENT = "MeshRouter";
|
|
13
|
+
function getCpuLoad() {
|
|
14
|
+
const avg = loadavg()[0];
|
|
15
|
+
const cores = cpus().length || 1;
|
|
16
|
+
return Math.min(1, avg / cores);
|
|
17
|
+
}
|
|
18
|
+
function createMeshRouter(broadcast) {
|
|
19
|
+
const router = Router();
|
|
20
|
+
router.get("/mesh/hello", async (_req, res) => {
|
|
21
|
+
const cfg = loadConfig();
|
|
22
|
+
if (!cfg.mesh.enabled) {
|
|
23
|
+
res.json({ titan: false, enabled: false });
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const { getOrCreateNodeId } = await import("../../mesh/identity.js");
|
|
27
|
+
const { getActiveRemoteTaskCount } = await import("../../mesh/transport.js");
|
|
28
|
+
const { discoverAllModels: discoverModels } = await import("../../providers/router.js");
|
|
29
|
+
const models = await discoverModels();
|
|
30
|
+
const { listAgents: meshListAgents } = await import("../../agent/multiAgent.js");
|
|
31
|
+
const cpuLoad = getCpuLoad();
|
|
32
|
+
const activeTasks = getActiveRemoteTaskCount();
|
|
33
|
+
const taskLoad = activeTasks / Math.max(cfg.mesh.maxRemoteTasks, 1);
|
|
34
|
+
const load = Math.min(1, cpuLoad * 0.4 + taskLoad * 0.6);
|
|
35
|
+
res.json({
|
|
36
|
+
titan: true,
|
|
37
|
+
nodeId: getOrCreateNodeId(),
|
|
38
|
+
version: TITAN_VERSION,
|
|
39
|
+
models: models.map((m) => m.id),
|
|
40
|
+
agentCount: meshListAgents().length,
|
|
41
|
+
load: Math.round(load * 100) / 100
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
router.get("/mesh/peers", async (_req, res) => {
|
|
45
|
+
const cfg = loadConfig();
|
|
46
|
+
if (!cfg.mesh.enabled) {
|
|
47
|
+
res.json({ peers: [], enabled: false });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const { getPeers } = await import("../../mesh/discovery.js");
|
|
51
|
+
res.json({ peers: getPeers(), enabled: true });
|
|
52
|
+
});
|
|
53
|
+
router.get("/mesh/models", async (_req, res) => {
|
|
54
|
+
const cfg = loadConfig();
|
|
55
|
+
if (!cfg.mesh.enabled) {
|
|
56
|
+
res.json({ models: [] });
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const { getMeshModels } = await import("../../mesh/registry.js");
|
|
60
|
+
res.json({ models: getMeshModels() });
|
|
61
|
+
});
|
|
62
|
+
router.get("/mesh/pending", async (_req, res) => {
|
|
63
|
+
const cfg = loadConfig();
|
|
64
|
+
if (!cfg.mesh.enabled) {
|
|
65
|
+
res.json({ pending: [], enabled: false });
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const { getPendingPeers } = await import("../../mesh/discovery.js");
|
|
69
|
+
res.json({ pending: getPendingPeers(), enabled: true });
|
|
70
|
+
});
|
|
71
|
+
router.post("/mesh/approve/:nodeId", async (req, res) => {
|
|
72
|
+
const cfg = loadConfig();
|
|
73
|
+
if (!cfg.mesh.enabled) {
|
|
74
|
+
res.status(400).json({ error: "Mesh not enabled" });
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const { approvePeer } = await import("../../mesh/discovery.js");
|
|
78
|
+
const peer = approvePeer(req.params.nodeId);
|
|
79
|
+
if (peer) {
|
|
80
|
+
broadcast({ type: "mesh_peer_approved", peer });
|
|
81
|
+
res.json({ approved: true, peer });
|
|
82
|
+
} else {
|
|
83
|
+
res.status(404).json({ error: "Peer not found in pending list or at max capacity" });
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
router.post("/mesh/reject/:nodeId", async (req, res) => {
|
|
87
|
+
const cfg = loadConfig();
|
|
88
|
+
if (!cfg.mesh.enabled) {
|
|
89
|
+
res.status(400).json({ error: "Mesh not enabled" });
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const { rejectPeer } = await import("../../mesh/discovery.js");
|
|
93
|
+
const rejected = rejectPeer(req.params.nodeId);
|
|
94
|
+
res.json({ rejected });
|
|
95
|
+
});
|
|
96
|
+
router.post("/mesh/revoke/:nodeId", async (req, res) => {
|
|
97
|
+
const cfg = loadConfig();
|
|
98
|
+
if (!cfg.mesh.enabled) {
|
|
99
|
+
res.status(400).json({ error: "Mesh not enabled" });
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const { revokePeer } = await import("../../mesh/discovery.js");
|
|
103
|
+
const revoked = revokePeer(req.params.nodeId);
|
|
104
|
+
if (revoked) {
|
|
105
|
+
broadcast({ type: "mesh_peer_revoked", nodeId: req.params.nodeId });
|
|
106
|
+
}
|
|
107
|
+
res.json({ revoked });
|
|
108
|
+
});
|
|
109
|
+
router.get("/mesh/status", async (_req, res) => {
|
|
110
|
+
const cfg = loadConfig();
|
|
111
|
+
if (!cfg.mesh.enabled) {
|
|
112
|
+
res.json({ enabled: false, status: "disabled" });
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const { getOrCreateNodeId } = await import("../../mesh/identity.js");
|
|
116
|
+
const { getPeers, getPendingPeers } = await import("../../mesh/discovery.js");
|
|
117
|
+
const { getConnectedPeerCount } = await import("../../mesh/transport.js");
|
|
118
|
+
const { getOrCreateNodeId: localNodeId } = await import("../../mesh/identity.js");
|
|
119
|
+
const nodeId = getOrCreateNodeId();
|
|
120
|
+
const approvedPeers = getPeers();
|
|
121
|
+
const pendingPeers = getPendingPeers();
|
|
122
|
+
const connectedCount = getConnectedPeerCount();
|
|
123
|
+
const connectedPeerIds = /* @__PURE__ */ new Set();
|
|
124
|
+
const peerDetails = approvedPeers.map((p) => {
|
|
125
|
+
const isConnected = p.lastSeen > Date.now() - 1e4;
|
|
126
|
+
if (isConnected) connectedPeerIds.add(p.nodeId);
|
|
127
|
+
return {
|
|
128
|
+
nodeId: p.nodeId,
|
|
129
|
+
hostname: p.hostname,
|
|
130
|
+
address: p.address,
|
|
131
|
+
port: p.port,
|
|
132
|
+
discoveredVia: p.discoveredVia,
|
|
133
|
+
lastSeen: p.lastSeen,
|
|
134
|
+
models: p.models,
|
|
135
|
+
agentCount: p.agentCount,
|
|
136
|
+
load: p.load,
|
|
137
|
+
isConnected
|
|
138
|
+
};
|
|
139
|
+
});
|
|
140
|
+
const totalApproved = approvedPeers.length;
|
|
141
|
+
const unreachableCount = totalApproved - connectedCount;
|
|
142
|
+
const healthScore = totalApproved > 0 ? Math.round((totalApproved - unreachableCount) / totalApproved * 100) / 100 : 1;
|
|
143
|
+
const discoveryModes = [];
|
|
144
|
+
if (cfg.mesh.mdns) discoveryModes.push("mdns");
|
|
145
|
+
if (cfg.mesh.tailscale) discoveryModes.push("tailscale");
|
|
146
|
+
if ((cfg.mesh.staticPeers || []).length > 0) discoveryModes.push("manual");
|
|
147
|
+
const status = unreachableCount === 0 && totalApproved > 0 ? "healthy" : unreachableCount > 0 && connectedCount > 0 ? "degraded" : totalApproved === 0 ? "empty" : "unreachable";
|
|
148
|
+
res.json({
|
|
149
|
+
enabled: true,
|
|
150
|
+
status,
|
|
151
|
+
nodeId,
|
|
152
|
+
discoveryModes,
|
|
153
|
+
peers: {
|
|
154
|
+
total: totalApproved,
|
|
155
|
+
connected: connectedCount,
|
|
156
|
+
unreachable: unreachableCount,
|
|
157
|
+
pending: pendingPeers.length
|
|
158
|
+
},
|
|
159
|
+
peerDetails,
|
|
160
|
+
healthScore,
|
|
161
|
+
maxPeers: cfg.mesh.maxPeers,
|
|
162
|
+
autoApprove: cfg.mesh.autoApprove
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
router.get("/mesh/routes", async (_req, res) => {
|
|
166
|
+
const cfg = loadConfig();
|
|
167
|
+
if (!cfg.mesh.enabled) {
|
|
168
|
+
res.json({ enabled: false, routes: [] });
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const { getRoutingTable } = await import("../../mesh/transport.js");
|
|
172
|
+
res.json({ routes: getRoutingTable() });
|
|
173
|
+
});
|
|
174
|
+
router.get("/homelab/machines", async (_req, res) => {
|
|
175
|
+
try {
|
|
176
|
+
const cfg = loadConfig();
|
|
177
|
+
const machines = cfg.homelab?.machines ?? [
|
|
178
|
+
{ name: "Titan PC", ip: "192.168.1.11", role: "Primary GPU (RTX 5090)", port: 48420, protocol: "https", path: "/api/health" },
|
|
179
|
+
{ name: "Mini PC", ip: "192.168.1.95", role: "Docker Host", port: 48420, protocol: "https", path: "/api/health" },
|
|
180
|
+
{ name: "T610 Server", ip: "192.168.1.67", role: "Always-on Backbone", port: 48420, protocol: "https", path: "/api/health" }
|
|
181
|
+
];
|
|
182
|
+
const https = await import("https");
|
|
183
|
+
const http = await import("http");
|
|
184
|
+
const probe = (protocol, ip, port, path) => {
|
|
185
|
+
return new Promise((resolve, reject) => {
|
|
186
|
+
const started = Date.now();
|
|
187
|
+
const lib = protocol === "https" ? https : http;
|
|
188
|
+
const req = lib.request({
|
|
189
|
+
host: ip,
|
|
190
|
+
port,
|
|
191
|
+
path,
|
|
192
|
+
method: "GET",
|
|
193
|
+
timeout: 3e3,
|
|
194
|
+
...protocol === "https" ? { rejectUnauthorized: false } : {}
|
|
195
|
+
}, (r) => {
|
|
196
|
+
let body = "";
|
|
197
|
+
r.on("data", (c) => body += c);
|
|
198
|
+
r.on("end", () => resolve({ ok: (r.statusCode ?? 0) >= 200 && (r.statusCode ?? 0) < 400, body, latencyMs: Date.now() - started }));
|
|
199
|
+
});
|
|
200
|
+
req.on("timeout", () => {
|
|
201
|
+
req.destroy(new Error("timeout"));
|
|
202
|
+
});
|
|
203
|
+
req.on("error", reject);
|
|
204
|
+
req.end();
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
const results = await Promise.all(machines.map(async (m) => {
|
|
208
|
+
const protocol = m.protocol ?? "https";
|
|
209
|
+
const port = m.port ?? 48420;
|
|
210
|
+
const path = m.path ?? "/api/health";
|
|
211
|
+
const started = Date.now();
|
|
212
|
+
try {
|
|
213
|
+
const r = await probe(protocol, m.ip, port, path);
|
|
214
|
+
let version;
|
|
215
|
+
try {
|
|
216
|
+
const parsed = JSON.parse(r.body);
|
|
217
|
+
version = parsed?.version;
|
|
218
|
+
} catch {
|
|
219
|
+
}
|
|
220
|
+
return { name: m.name, ip: m.ip, role: m.role ?? "", online: r.ok, latencyMs: r.latencyMs, version };
|
|
221
|
+
} catch (err) {
|
|
222
|
+
return { name: m.name, ip: m.ip, role: m.role ?? "", online: false, latencyMs: Date.now() - started, error: err.message };
|
|
223
|
+
}
|
|
224
|
+
}));
|
|
225
|
+
res.json({ machines: results });
|
|
226
|
+
} catch (err) {
|
|
227
|
+
res.status(500).json({ error: err.message });
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
router.get("/dependencies/scan", async (_req, res) => {
|
|
231
|
+
try {
|
|
232
|
+
const reportPath = join(TITAN_WORKSPACE, "dependency-scan-report.json");
|
|
233
|
+
if (fs.existsSync(reportPath)) {
|
|
234
|
+
const report = JSON.parse(fs.readFileSync(reportPath, "utf-8"));
|
|
235
|
+
res.json(report);
|
|
236
|
+
} else {
|
|
237
|
+
res.status(404).json({ error: "No scan report found. Run a scan first." });
|
|
238
|
+
}
|
|
239
|
+
} catch (e) {
|
|
240
|
+
logger.error(COMPONENT, `Endpoint error: ${e.message}`);
|
|
241
|
+
res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
router.post("/dependencies/scan", async (req, res) => {
|
|
245
|
+
try {
|
|
246
|
+
const { fix = false } = req.body;
|
|
247
|
+
const scriptPath = join(dirname(fileURLToPath(import.meta.url)), "../../scripts/dependency-scan.cjs");
|
|
248
|
+
const proc = spawn("node", [scriptPath, ...fix ? ["--fix"] : []], {
|
|
249
|
+
cwd: TITAN_WORKSPACE,
|
|
250
|
+
stdio: "pipe",
|
|
251
|
+
detached: false
|
|
252
|
+
});
|
|
253
|
+
let output = "";
|
|
254
|
+
proc.stdout?.on("data", (data) => {
|
|
255
|
+
output += data.toString();
|
|
256
|
+
});
|
|
257
|
+
proc.stderr?.on("data", (data) => {
|
|
258
|
+
output += data.toString();
|
|
259
|
+
});
|
|
260
|
+
proc.on("close", (code) => {
|
|
261
|
+
if (code === 0) {
|
|
262
|
+
logger.info(COMPONENT, "Dependency scan completed successfully");
|
|
263
|
+
} else {
|
|
264
|
+
logger.warn(COMPONENT, `Dependency scan exited with code ${code}`);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
res.json({ success: true, message: "Dependency scan started in background" });
|
|
268
|
+
} catch (e) {
|
|
269
|
+
logger.error(COMPONENT, `Endpoint error: ${e.message}`);
|
|
270
|
+
res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
router.get("/dependencies/status", (_req, res) => {
|
|
274
|
+
try {
|
|
275
|
+
const reportPath = join(TITAN_WORKSPACE, "dependency-scan-report.json");
|
|
276
|
+
if (fs.existsSync(reportPath)) {
|
|
277
|
+
const stats = fs.statSync(reportPath);
|
|
278
|
+
const report = JSON.parse(fs.readFileSync(reportPath, "utf-8"));
|
|
279
|
+
const summary = {
|
|
280
|
+
lastScan: report.timestamp,
|
|
281
|
+
lastScanAge: Date.now() - new Date(report.timestamp).getTime(),
|
|
282
|
+
vulnerabilities: report.vulnerabilities.total,
|
|
283
|
+
critical: report.vulnerabilities.critical,
|
|
284
|
+
high: report.vulnerabilities.high,
|
|
285
|
+
outdated: report.outdated?.length || 0,
|
|
286
|
+
deprecated: report.deprecated?.length || 0,
|
|
287
|
+
licenseIssues: report.licenseIssues?.length || 0,
|
|
288
|
+
health: report.vulnerabilities.critical === 0 && report.vulnerabilities.high === 0 ? "healthy" : "warning"
|
|
289
|
+
};
|
|
290
|
+
res.json(summary);
|
|
291
|
+
} else {
|
|
292
|
+
res.json({
|
|
293
|
+
lastScan: null,
|
|
294
|
+
health: "unknown",
|
|
295
|
+
message: "No scan has been run yet"
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
} catch (e) {
|
|
299
|
+
logger.error(COMPONENT, `Endpoint error: ${e.message}`);
|
|
300
|
+
res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
router.get("/wiki/entities", (req, res) => {
|
|
304
|
+
try {
|
|
305
|
+
const type = req.query.type;
|
|
306
|
+
const q = req.query.q;
|
|
307
|
+
let entities = listEntities(type || void 0);
|
|
308
|
+
if (q) {
|
|
309
|
+
const query = q.toLowerCase();
|
|
310
|
+
entities = entities.filter(
|
|
311
|
+
(e) => e.name.toLowerCase().includes(query) || e.facts.some((f) => f.toLowerCase().includes(query)) || (e.summary || "").toLowerCase().includes(query)
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
res.json(entities.map((e) => ({
|
|
315
|
+
id: e.id,
|
|
316
|
+
name: e.name,
|
|
317
|
+
type: e.type,
|
|
318
|
+
summary: e.summary,
|
|
319
|
+
factCount: e.facts.length,
|
|
320
|
+
aliases: e.aliases,
|
|
321
|
+
firstSeen: e.firstSeen,
|
|
322
|
+
lastSeen: e.lastSeen
|
|
323
|
+
})));
|
|
324
|
+
} catch (e) {
|
|
325
|
+
logger.error(COMPONENT, `Endpoint error: ${e.message}`);
|
|
326
|
+
res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
router.get("/wiki/entity/:name", (req, res) => {
|
|
330
|
+
try {
|
|
331
|
+
const entity = getEntity(decodeURIComponent(req.params.name));
|
|
332
|
+
if (!entity) {
|
|
333
|
+
res.status(404).json({ error: "Entity not found" });
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const graphData = getGraphData();
|
|
337
|
+
const relatedEdges = graphData.edges.filter((e) => e.from === entity.id || e.to === entity.id);
|
|
338
|
+
const relatedIds = new Set(relatedEdges.map((e) => e.from === entity.id ? e.to : e.from));
|
|
339
|
+
const related = graphData.nodes.filter((n) => relatedIds.has(n.id)).map((n) => ({
|
|
340
|
+
id: n.id,
|
|
341
|
+
name: n.label,
|
|
342
|
+
type: n.type,
|
|
343
|
+
relation: relatedEdges.find((e) => e.from === entity.id && e.to === n.id || e.to === entity.id && e.from === n.id)?.label || "co_mentioned"
|
|
344
|
+
}));
|
|
345
|
+
const episodes = getEntityEpisodes(entity.id, 20).map((ep) => ({
|
|
346
|
+
id: ep.id,
|
|
347
|
+
content: ep.content.slice(0, 300),
|
|
348
|
+
source: ep.source,
|
|
349
|
+
createdAt: ep.createdAt
|
|
350
|
+
}));
|
|
351
|
+
res.json({ ...entity, related, episodes });
|
|
352
|
+
} catch (e) {
|
|
353
|
+
logger.error(COMPONENT, `Endpoint error: ${e.message}`);
|
|
354
|
+
res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
router.get("/agent-templates", async (_req, res) => {
|
|
358
|
+
try {
|
|
359
|
+
const { listTemplates, BUILTIN_TEMPLATES } = await import("../../skills/agentTemplates.js");
|
|
360
|
+
const installed = listTemplates();
|
|
361
|
+
res.json({ builtin: BUILTIN_TEMPLATES, installed });
|
|
362
|
+
} catch (e) {
|
|
363
|
+
logger.error(COMPONENT, `Endpoint error: ${e.message}`);
|
|
364
|
+
res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
router.post("/agent-templates", async (req, res) => {
|
|
368
|
+
try {
|
|
369
|
+
const { saveTemplate } = await import("../../skills/agentTemplates.js");
|
|
370
|
+
saveTemplate(req.body);
|
|
371
|
+
res.json({ success: true });
|
|
372
|
+
} catch (e) {
|
|
373
|
+
logger.error(COMPONENT, `Endpoint error: ${e.message}`);
|
|
374
|
+
res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
router.get("/training/stats", async (_req, res) => {
|
|
378
|
+
try {
|
|
379
|
+
const { getTrainingStats } = await import("../../agent/trajectoryCapture.js");
|
|
380
|
+
res.json(getTrainingStats());
|
|
381
|
+
} catch (e) {
|
|
382
|
+
logger.error(COMPONENT, `Endpoint error: ${e.message}`);
|
|
383
|
+
res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
router.get("/training/export", async (_req, res) => {
|
|
387
|
+
try {
|
|
388
|
+
const { exportTrainingData } = await import("../../agent/trajectoryCapture.js");
|
|
389
|
+
res.setHeader("Content-Type", "application/jsonl");
|
|
390
|
+
res.setHeader("Content-Disposition", 'attachment; filename="titan-training-data.jsonl"');
|
|
391
|
+
res.send(exportTrainingData());
|
|
392
|
+
} catch (e) {
|
|
393
|
+
logger.error(COMPONENT, `Endpoint error: ${e.message}`);
|
|
394
|
+
res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
router.get("/dreaming/status", async (_req, res) => {
|
|
398
|
+
try {
|
|
399
|
+
const { getDreamingStatus } = await import("../../memory/dreaming.js");
|
|
400
|
+
res.json(getDreamingStatus());
|
|
401
|
+
} catch (e) {
|
|
402
|
+
logger.error(COMPONENT, `Endpoint error: ${e.message}`);
|
|
403
|
+
res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
router.post("/dreaming/run", async (_req, res) => {
|
|
407
|
+
try {
|
|
408
|
+
const { runConsolidation } = await import("../../memory/dreaming.js");
|
|
409
|
+
const result = await runConsolidation();
|
|
410
|
+
res.json({ success: true, ...result });
|
|
411
|
+
} catch (e) {
|
|
412
|
+
logger.error(COMPONENT, `Endpoint error: ${e.message}`);
|
|
413
|
+
res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
router.get("/dreaming/history", async (_req, res) => {
|
|
417
|
+
try {
|
|
418
|
+
const { getConsolidationHistory } = await import("../../memory/dreaming.js");
|
|
419
|
+
res.json(getConsolidationHistory());
|
|
420
|
+
} catch (e) {
|
|
421
|
+
logger.error(COMPONENT, `Endpoint error: ${e.message}`);
|
|
422
|
+
res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
router.post("/backup/create", async (_req, res) => {
|
|
426
|
+
try {
|
|
427
|
+
const { createBackup } = await import("../../storage/backup.js");
|
|
428
|
+
const info = await createBackup();
|
|
429
|
+
res.json({ success: true, ...info });
|
|
430
|
+
} catch (e) {
|
|
431
|
+
logger.error(COMPONENT, `Endpoint error: ${e.message}`);
|
|
432
|
+
res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
router.get("/backup/list", async (_req, res) => {
|
|
436
|
+
try {
|
|
437
|
+
const { listBackups } = await import("../../storage/backup.js");
|
|
438
|
+
res.json({ backups: listBackups() });
|
|
439
|
+
} catch (e) {
|
|
440
|
+
logger.error(COMPONENT, `Endpoint error: ${e.message}`);
|
|
441
|
+
res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
router.post("/backup/verify", async (req, res) => {
|
|
445
|
+
try {
|
|
446
|
+
const { verifyBackup, listBackups } = await import("../../storage/backup.js");
|
|
447
|
+
const path = req.body?.path || listBackups()[0]?.path;
|
|
448
|
+
if (!path) {
|
|
449
|
+
res.status(400).json({ error: "No backup path specified and no backups found" });
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const result = await verifyBackup(path);
|
|
453
|
+
res.json(result);
|
|
454
|
+
} catch (e) {
|
|
455
|
+
logger.error(COMPONENT, `Endpoint error: ${e.message}`);
|
|
456
|
+
res.status(500).json({ error: "Something went wrong on our end. Please try again in a moment." });
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
return router;
|
|
460
|
+
}
|
|
461
|
+
export {
|
|
462
|
+
createMeshRouter
|
|
463
|
+
};
|
|
464
|
+
//# sourceMappingURL=mesh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/gateway/routes/mesh.ts"],"sourcesContent":["/**\n * Mesh Router + Misc Routes\n *\n * Extracted from gateway/server.ts.\n * Consolidates /api/mesh/* and several miscellaneous endpoints.\n */\n\nimport { Router } from 'express';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\nimport { homedir } from 'os';\nimport { loadavg, cpus } from 'os';\nimport fs from 'fs';\nimport { spawn } from 'child_process';\nimport { loadConfig } from '../../config/config.js';\nimport { TITAN_VERSION, TITAN_WORKSPACE, TITAN_HOME } from '../../utils/constants.js';\nimport logger from '../../utils/logger.js';\nimport { listEntities, getEntity, getGraphData, getEntityEpisodes } from '../../memory/graph.js';\n\nconst COMPONENT = 'MeshRouter';\n\n/** Get normalized CPU load (0.0–1.0) using 1-minute load average */\nfunction getCpuLoad(): number {\n const avg = loadavg()[0]; // 1-minute load average\n const cores = cpus().length || 1;\n return Math.min(1, avg / cores);\n}\n\nexport function createMeshRouter(broadcast: (data: Record<string, unknown>, userId?: string) => void): Router {\n const router = Router();\n\n // ── Mesh Networking Endpoints ─────────────────────────────────\n router.get('/mesh/hello', async (_req, res) => {\n const cfg = loadConfig();\n if (!cfg.mesh.enabled) { res.json({ titan: false, enabled: false }); return; }\n const { getOrCreateNodeId } = await import('../../mesh/identity.js');\n const { getActiveRemoteTaskCount } = await import('../../mesh/transport.js');\n const { discoverAllModels: discoverModels } = await import('../../providers/router.js');\n const models = await discoverModels();\n const { listAgents: meshListAgents } = await import('../../agent/multiAgent.js');\n const cpuLoad = getCpuLoad();\n const activeTasks = getActiveRemoteTaskCount();\n // Load score: 0.0 (idle) to 1.0 (maxed). Blend CPU + task saturation.\n const taskLoad = activeTasks / Math.max(cfg.mesh.maxRemoteTasks, 1);\n const load = Math.min(1, cpuLoad * 0.4 + taskLoad * 0.6);\n res.json({\n titan: true,\n nodeId: getOrCreateNodeId(),\n version: TITAN_VERSION,\n models: models.map(m => m.id),\n agentCount: meshListAgents().length,\n load: Math.round(load * 100) / 100,\n });\n });\n\n router.get('/mesh/peers', async (_req, res) => {\n const cfg = loadConfig();\n if (!cfg.mesh.enabled) { res.json({ peers: [], enabled: false }); return; }\n const { getPeers } = await import('../../mesh/discovery.js');\n res.json({ peers: getPeers(), enabled: true });\n });\n\n router.get('/mesh/models', async (_req, res) => {\n const cfg = loadConfig();\n if (!cfg.mesh.enabled) { res.json({ models: [] }); return; }\n const { getMeshModels } = await import('../../mesh/registry.js');\n res.json({ models: getMeshModels() });\n });\n\n router.get('/mesh/pending', async (_req, res) => {\n const cfg = loadConfig();\n if (!cfg.mesh.enabled) { res.json({ pending: [], enabled: false }); return; }\n const { getPendingPeers } = await import('../../mesh/discovery.js');\n res.json({ pending: getPendingPeers(), enabled: true });\n });\n\n router.post('/mesh/approve/:nodeId', async (req, res) => {\n const cfg = loadConfig();\n if (!cfg.mesh.enabled) { res.status(400).json({ error: 'Mesh not enabled' }); return; }\n const { approvePeer } = await import('../../mesh/discovery.js');\n const peer = approvePeer(req.params.nodeId);\n if (peer) {\n broadcast({ type: 'mesh_peer_approved', peer });\n res.json({ approved: true, peer });\n } else {\n res.status(404).json({ error: 'Peer not found in pending list or at max capacity' });\n }\n });\n\n router.post('/mesh/reject/:nodeId', async (req, res) => {\n const cfg = loadConfig();\n if (!cfg.mesh.enabled) { res.status(400).json({ error: 'Mesh not enabled' }); return; }\n const { rejectPeer } = await import('../../mesh/discovery.js');\n const rejected = rejectPeer(req.params.nodeId);\n res.json({ rejected });\n });\n\n router.post('/mesh/revoke/:nodeId', async (req, res) => {\n const cfg = loadConfig();\n if (!cfg.mesh.enabled) { res.status(400).json({ error: 'Mesh not enabled' }); return; }\n const { revokePeer } = await import('../../mesh/discovery.js');\n const revoked = revokePeer(req.params.nodeId);\n if (revoked) {\n broadcast({ type: 'mesh_peer_revoked', nodeId: req.params.nodeId });\n }\n res.json({ revoked });\n });\n\n // ── Mesh Health / Status Endpoint ─────────────────────────────\n router.get('/mesh/status', async (_req, res) => {\n const cfg = loadConfig();\n if (!cfg.mesh.enabled) { res.json({ enabled: false, status: 'disabled' }); return; }\n\n const { getOrCreateNodeId } = await import('../../mesh/identity.js');\n const { getPeers, getPendingPeers } = await import('../../mesh/discovery.js');\n const { getConnectedPeerCount } = await import('../../mesh/transport.js');\n const { getOrCreateNodeId: localNodeId } = await import('../../mesh/identity.js');\n\n const nodeId = getOrCreateNodeId();\n const approvedPeers = getPeers();\n const pendingPeers = getPendingPeers();\n const connectedCount = getConnectedPeerCount();\n const connectedPeerIds = new Set<string>();\n\n // Collect per-peer connection detail from approved list + transport\n const peerDetails = approvedPeers.map(p => {\n const isConnected = p.lastSeen > Date.now() - 10_000; // Consider connected if seen in last 10s\n if (isConnected) connectedPeerIds.add(p.nodeId);\n return {\n nodeId: p.nodeId,\n hostname: p.hostname,\n address: p.address,\n port: p.port,\n discoveredVia: p.discoveredVia,\n lastSeen: p.lastSeen,\n models: p.models,\n agentCount: p.agentCount,\n load: p.load,\n isConnected,\n };\n });\n\n // Composite health score\n const totalApproved = approvedPeers.length;\n const unreachableCount = totalApproved - connectedCount;\n const healthScore = totalApproved > 0\n ? Math.round(((totalApproved - unreachableCount) / totalApproved) * 100) / 100\n : 1.0;\n\n // Discovery mode detection\n const discoveryModes: string[] = [];\n if (cfg.mesh.mdns) discoveryModes.push('mdns');\n if (cfg.mesh.tailscale) discoveryModes.push('tailscale');\n if ((cfg.mesh.staticPeers || []).length > 0) discoveryModes.push('manual');\n\n const status = unreachableCount === 0 && totalApproved > 0\n ? 'healthy'\n : unreachableCount > 0 && connectedCount > 0\n ? 'degraded'\n : totalApproved === 0\n ? 'empty'\n : 'unreachable';\n\n res.json({\n enabled: true,\n status,\n nodeId,\n discoveryModes,\n peers: {\n total: totalApproved,\n connected: connectedCount,\n unreachable: unreachableCount,\n pending: pendingPeers.length,\n },\n peerDetails,\n healthScore,\n maxPeers: cfg.mesh.maxPeers,\n autoApprove: cfg.mesh.autoApprove,\n });\n });\n\n // ── Mesh Routes Endpoint ───────────────────────────────────────\n router.get('/mesh/routes', async (_req, res) => {\n const cfg = loadConfig();\n if (!cfg.mesh.enabled) { res.json({ enabled: false, routes: [] }); return; }\n const { getRoutingTable } = await import('../../mesh/transport.js');\n res.json({ routes: getRoutingTable() });\n });\n\n // ── Homelab machine health (v4.8.4) ───────────────────────────\n router.get('/homelab/machines', async (_req, res) => {\n try {\n const cfg = loadConfig() as unknown as {\n homelab?: { machines?: Array<{ name: string; ip: string; role?: string; port?: number; protocol?: 'http' | 'https'; path?: string }> };\n };\n const machines = cfg.homelab?.machines ?? [\n { name: 'Titan PC', ip: '192.168.1.11', role: 'Primary GPU (RTX 5090)', port: 48420, protocol: 'https' as const, path: '/api/health' },\n { name: 'Mini PC', ip: '192.168.1.95', role: 'Docker Host', port: 48420, protocol: 'https' as const, path: '/api/health' },\n { name: 'T610 Server', ip: '192.168.1.67', role: 'Always-on Backbone', port: 48420, protocol: 'https' as const, path: '/api/health' },\n ];\n const https = await import('https');\n const http = await import('http');\n const probe = (protocol: 'http' | 'https', ip: string, port: number, path: string): Promise<{ ok: boolean; body: string; latencyMs: number }> => {\n return new Promise((resolve, reject) => {\n const started = Date.now();\n const lib = protocol === 'https' ? https : http;\n const req = lib.request({\n host: ip,\n port,\n path,\n method: 'GET',\n timeout: 3000,\n ...(protocol === 'https' ? { rejectUnauthorized: false } : {}),\n }, (r) => {\n let body = '';\n r.on('data', (c) => body += c);\n r.on('end', () => resolve({ ok: (r.statusCode ?? 0) >= 200 && (r.statusCode ?? 0) < 400, body, latencyMs: Date.now() - started }));\n });\n req.on('timeout', () => { req.destroy(new Error('timeout')); });\n req.on('error', reject);\n req.end();\n });\n };\n const results = await Promise.all(machines.map(async (m) => {\n const protocol = m.protocol ?? 'https';\n const port = m.port ?? 48420;\n const path = m.path ?? '/api/health';\n const started = Date.now();\n try {\n const r = await probe(protocol, m.ip, port, path);\n let version: string | undefined;\n try {\n const parsed = JSON.parse(r.body) as { version?: string };\n version = parsed?.version;\n } catch { /* not JSON — still online */ }\n return { name: m.name, ip: m.ip, role: m.role ?? '', online: r.ok, latencyMs: r.latencyMs, version };\n } catch (err) {\n return { name: m.name, ip: m.ip, role: m.role ?? '', online: false, latencyMs: Date.now() - started, error: (err as Error).message };\n }\n }));\n res.json({ machines: results });\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n // ── Dependency Scan API ────────────────────────────────────────\n router.get('/dependencies/scan', async (_req, res) => {\n try {\n const reportPath = join(TITAN_WORKSPACE, 'dependency-scan-report.json');\n if (fs.existsSync(reportPath)) {\n const report = JSON.parse(fs.readFileSync(reportPath, 'utf-8'));\n res.json(report);\n } else {\n res.status(404).json({ error: 'No scan report found. Run a scan first.' });\n }\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.post('/dependencies/scan', async (req, res) => {\n try {\n const { fix = false } = req.body;\n const scriptPath = join(dirname(fileURLToPath(import.meta.url)), '../../scripts/dependency-scan.cjs');\n\n const proc = spawn('node', [scriptPath, ...(fix ? ['--fix'] : [])], {\n cwd: TITAN_WORKSPACE,\n stdio: 'pipe',\n detached: false,\n });\n\n let output = '';\n proc.stdout?.on('data', (data) => { output += data.toString(); });\n proc.stderr?.on('data', (data) => { output += data.toString(); });\n\n proc.on('close', (code) => {\n if (code === 0) {\n logger.info(COMPONENT, 'Dependency scan completed successfully');\n } else {\n logger.warn(COMPONENT, `Dependency scan exited with code ${code}`);\n }\n });\n\n res.json({ success: true, message: 'Dependency scan started in background' });\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n router.get('/dependencies/status', (_req, res) => {\n try {\n const reportPath = join(TITAN_WORKSPACE, 'dependency-scan-report.json');\n if (fs.existsSync(reportPath)) {\n const stats = fs.statSync(reportPath);\n const report = JSON.parse(fs.readFileSync(reportPath, 'utf-8'));\n\n const summary = {\n lastScan: report.timestamp,\n lastScanAge: Date.now() - new Date(report.timestamp).getTime(),\n vulnerabilities: report.vulnerabilities.total,\n critical: report.vulnerabilities.critical,\n high: report.vulnerabilities.high,\n outdated: report.outdated?.length || 0,\n deprecated: report.deprecated?.length || 0,\n licenseIssues: report.licenseIssues?.length || 0,\n health: report.vulnerabilities.critical === 0 && report.vulnerabilities.high === 0 ? 'healthy' : 'warning',\n };\n\n res.json(summary);\n } else {\n res.json({\n lastScan: null,\n health: 'unknown',\n message: 'No scan has been run yet',\n });\n }\n } catch (e) {\n logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' });\n }\n });\n\n // Memory Wiki API — browseable knowledge base\n router.get('/wiki/entities', (req, res) => {\n try {\n const type = req.query.type as string | undefined;\n const q = req.query.q as string | undefined;\n let entities = listEntities(type || undefined);\n if (q) {\n const query = q.toLowerCase();\n entities = entities.filter(e =>\n e.name.toLowerCase().includes(query) ||\n e.facts.some(f => f.toLowerCase().includes(query)) ||\n (e.summary || '').toLowerCase().includes(query)\n );\n }\n res.json(entities.map(e => ({\n id: e.id,\n name: e.name,\n type: e.type,\n summary: e.summary,\n factCount: e.facts.length,\n aliases: e.aliases,\n firstSeen: e.firstSeen,\n lastSeen: e.lastSeen,\n })));\n } catch (e) { logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' }); }\n });\n\n router.get('/wiki/entity/:name', (req, res) => {\n try {\n const entity = getEntity(decodeURIComponent(req.params.name));\n if (!entity) { res.status(404).json({ error: 'Entity not found' }); return; }\n const graphData = getGraphData();\n const relatedEdges = graphData.edges.filter(e => e.from === entity.id || e.to === entity.id);\n const relatedIds = new Set(relatedEdges.map(e => e.from === entity.id ? e.to : e.from));\n const related = graphData.nodes.filter(n => relatedIds.has(n.id)).map(n => ({\n id: n.id,\n name: n.label,\n type: n.type,\n relation: relatedEdges.find(e => (e.from === entity.id && e.to === n.id) || (e.to === entity.id && e.from === n.id))?.label || 'co_mentioned',\n }));\n const episodes = getEntityEpisodes(entity.id, 20).map(ep => ({\n id: ep.id,\n content: ep.content.slice(0, 300),\n source: ep.source,\n createdAt: ep.createdAt,\n }));\n res.json({ ...entity, related, episodes });\n } catch (e) { logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' }); }\n });\n\n // Agent Templates (marketplace)\n router.get('/agent-templates', async (_req, res) => {\n try {\n const { listTemplates, BUILTIN_TEMPLATES } = await import('../../skills/agentTemplates.js');\n const installed = listTemplates();\n res.json({ builtin: BUILTIN_TEMPLATES, installed });\n } catch (e) { logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' }); }\n });\n\n router.post('/agent-templates', async (req, res) => {\n try {\n const { saveTemplate } = await import('../../skills/agentTemplates.js');\n saveTemplate(req.body);\n res.json({ success: true });\n } catch (e) { logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' }); }\n });\n\n // Training data (RL trajectory capture)\n router.get('/training/stats', async (_req, res) => {\n try {\n const { getTrainingStats } = await import('../../agent/trajectoryCapture.js');\n res.json(getTrainingStats());\n } catch (e) { logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' }); }\n });\n\n router.get('/training/export', async (_req, res) => {\n try {\n const { exportTrainingData } = await import('../../agent/trajectoryCapture.js');\n res.setHeader('Content-Type', 'application/jsonl');\n res.setHeader('Content-Disposition', 'attachment; filename=\"titan-training-data.jsonl\"');\n res.send(exportTrainingData());\n } catch (e) { logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' }); }\n });\n\n // Dreaming memory (sleep-cycle consolidation)\n router.get('/dreaming/status', async (_req, res) => {\n try {\n const { getDreamingStatus } = await import('../../memory/dreaming.js');\n res.json(getDreamingStatus());\n } catch (e) { logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' }); }\n });\n\n router.post('/dreaming/run', async (_req, res) => {\n try {\n const { runConsolidation } = await import('../../memory/dreaming.js');\n const result = await runConsolidation();\n res.json({ success: true, ...result });\n } catch (e) { logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' }); }\n });\n\n router.get('/dreaming/history', async (_req, res) => {\n try {\n const { getConsolidationHistory } = await import('../../memory/dreaming.js');\n res.json(getConsolidationHistory());\n } catch (e) { logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' }); }\n });\n\n // Backup system\n router.post('/backup/create', async (_req, res) => {\n try {\n const { createBackup } = await import('../../storage/backup.js');\n const info = await createBackup();\n res.json({ success: true, ...info });\n } catch (e) { logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' }); }\n });\n\n router.get('/backup/list', async (_req, res) => {\n try {\n const { listBackups } = await import('../../storage/backup.js');\n res.json({ backups: listBackups() });\n } catch (e) { logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' }); }\n });\n\n router.post('/backup/verify', async (req, res) => {\n try {\n const { verifyBackup, listBackups } = await import('../../storage/backup.js');\n const path = req.body?.path || listBackups()[0]?.path;\n if (!path) { res.status(400).json({ error: 'No backup path specified and no backups found' }); return; }\n const result = await verifyBackup(path);\n res.json(result);\n } catch (e) { logger.error(COMPONENT, `Endpoint error: ${(e as Error).message}`); res.status(500).json({ error: 'Something went wrong on our end. Please try again in a moment.' }); }\n });\n\n return router;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Router } from "express";
|
|
3
|
+
import { serializePrometheus, getMetricsSummary } from "../metrics.js";
|
|
4
|
+
import { loadConfig, updateConfig } from "../../config/config.js";
|
|
5
|
+
import { collectSystemProfile, getRemoteAnalyticsStatus } from "../../analytics/collector.js";
|
|
6
|
+
import { TITAN_VERSION } from "../../utils/constants.js";
|
|
7
|
+
function getUserIdFromReq(req) {
|
|
8
|
+
const token = req.headers.authorization?.replace("Bearer ", "");
|
|
9
|
+
if (token) {
|
|
10
|
+
return token;
|
|
11
|
+
}
|
|
12
|
+
return "default-user";
|
|
13
|
+
}
|
|
14
|
+
function createMetricsRouter() {
|
|
15
|
+
const router = Router();
|
|
16
|
+
router.get("/metrics", (_req, res) => {
|
|
17
|
+
res.setHeader("Content-Type", "text/plain; version=0.0.4");
|
|
18
|
+
res.send(serializePrometheus());
|
|
19
|
+
});
|
|
20
|
+
router.get("/metrics/summary", (_req, res) => {
|
|
21
|
+
res.json(getMetricsSummary());
|
|
22
|
+
});
|
|
23
|
+
router.post("/telemetry", (req, res) => {
|
|
24
|
+
const cfg = loadConfig();
|
|
25
|
+
if (!cfg.telemetry?.enabled) {
|
|
26
|
+
res.status(204).end();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const { event, properties, timestamp } = req.body || {};
|
|
30
|
+
if (!event || typeof event !== "string") {
|
|
31
|
+
res.status(400).json({ error: "event is required" });
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const entry = {
|
|
35
|
+
event,
|
|
36
|
+
properties: properties || {},
|
|
37
|
+
timestamp: timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
38
|
+
sessionId: getUserIdFromReq(req)
|
|
39
|
+
};
|
|
40
|
+
import("../../storage/index.js").then(({ getStorage }) => getStorage()).then((storage) => storage.appendTelemetryEvent?.(entry)).catch(() => {
|
|
41
|
+
});
|
|
42
|
+
res.status(204).end();
|
|
43
|
+
});
|
|
44
|
+
router.get("/telemetry/events", async (_req, res) => {
|
|
45
|
+
const cfg = loadConfig();
|
|
46
|
+
if (!cfg.telemetry?.enabled) {
|
|
47
|
+
res.json({ enabled: false, events: [] });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const limit = Math.min(parseInt(_req.query.limit || "100", 10), 1e3);
|
|
51
|
+
try {
|
|
52
|
+
const { getStorage } = await import("../../storage/index.js");
|
|
53
|
+
const storage = await getStorage();
|
|
54
|
+
const events = await storage.queryTelemetryEvents?.({ limit }) ?? [];
|
|
55
|
+
res.json({ enabled: true, events });
|
|
56
|
+
} catch (err) {
|
|
57
|
+
res.status(500).json({ error: err.message });
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
router.get("/analytics/profile", async (_req, res) => {
|
|
61
|
+
try {
|
|
62
|
+
const profile = await collectSystemProfile();
|
|
63
|
+
res.json(profile);
|
|
64
|
+
} catch (err) {
|
|
65
|
+
res.status(500).json({ error: err.message });
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
router.post("/telemetry/consent", async (req, res) => {
|
|
69
|
+
try {
|
|
70
|
+
const body = req.body || {};
|
|
71
|
+
const enabled = body.enabled === true;
|
|
72
|
+
const crashReports = body.crashReports !== false;
|
|
73
|
+
const patch = {
|
|
74
|
+
telemetry: {
|
|
75
|
+
enabled,
|
|
76
|
+
crashReports,
|
|
77
|
+
consentedAt: enabled ? (/* @__PURE__ */ new Date()).toISOString() : void 0,
|
|
78
|
+
consentedVersion: enabled ? TITAN_VERSION : void 0
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
updateConfig(patch);
|
|
82
|
+
if (enabled) {
|
|
83
|
+
(async () => {
|
|
84
|
+
try {
|
|
85
|
+
const { recordStartupAnalytics: record } = await import("../../analytics/collector.js");
|
|
86
|
+
await record();
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
})();
|
|
90
|
+
}
|
|
91
|
+
res.json({ ok: true, enabled, crashReports });
|
|
92
|
+
} catch (err) {
|
|
93
|
+
res.status(500).json({ error: err.message });
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
router.get("/telemetry/consent", (_req, res) => {
|
|
97
|
+
const cfg = loadConfig();
|
|
98
|
+
const t = cfg.telemetry;
|
|
99
|
+
res.json({
|
|
100
|
+
enabled: Boolean(t?.enabled),
|
|
101
|
+
crashReports: t?.crashReports !== false,
|
|
102
|
+
consentedAt: t?.consentedAt,
|
|
103
|
+
consentedVersion: t?.consentedVersion,
|
|
104
|
+
remoteUrl: t?.remoteUrl
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
router.get("/telemetry/status", async (_req, res) => {
|
|
108
|
+
try {
|
|
109
|
+
const cfg = loadConfig();
|
|
110
|
+
const t = cfg.telemetry;
|
|
111
|
+
const remote = getRemoteAnalyticsStatus();
|
|
112
|
+
res.json({
|
|
113
|
+
consent: {
|
|
114
|
+
enabled: Boolean(t?.enabled),
|
|
115
|
+
crashReports: t?.crashReports !== false,
|
|
116
|
+
consentedAt: t?.consentedAt,
|
|
117
|
+
consentedVersion: t?.consentedVersion,
|
|
118
|
+
remoteUrl: t?.remoteUrl
|
|
119
|
+
},
|
|
120
|
+
remote
|
|
121
|
+
});
|
|
122
|
+
} catch (err) {
|
|
123
|
+
res.status(500).json({ error: err.message });
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
return router;
|
|
127
|
+
}
|
|
128
|
+
export {
|
|
129
|
+
createMetricsRouter
|
|
130
|
+
};
|
|
131
|
+
//# sourceMappingURL=metricsRouter.js.map
|