titan-agent 5.4.2 → 5.5.5
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/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/killSwitch.js +2 -2
- package/dist/safety/killSwitch.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 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/gateway/routes/files.ts"],"sourcesContent":["/**\n * Files Router\n *\n * Extracted from gateway/server.ts v5.5.0.\n */\n\nimport { Router } from 'express';\nimport { homedir } from 'os';\nimport { join, dirname, resolve } from 'path';\nimport fs from 'fs';\nimport logger from '../../utils/logger.js';\nimport { loadConfig } from '../../config/config.js';\n\nconst COMPONENT = 'FilesRouter';\n\nfunction getFileRoots(): Array<{ label: string; path: string }> {\n const cfg = loadConfig();\n const fmCfg = (cfg as Record<string, unknown>).fileManager as { roots?: string[]; blockedPatterns?: string[] } | undefined;\n const roots = fmCfg?.roots || ['~/.titan'];\n return roots.map(r => {\n const expanded = r.replace(/^~/, homedir());\n const abs = resolve(expanded);\n const label = abs.split('/').filter(Boolean).pop() || abs;\n return { label, path: abs };\n });\n}\n\nfunction validateFilePath(reqPath: string, rootParam?: string): { valid: boolean; fullPath: string; basePath: string; error?: string } {\n const roots = getFileRoots();\n if (roots.length === 0) return { valid: false, fullPath: '', basePath: '', error: 'No file roots configured' };\n\n let selectedRoot = roots[0];\n if (rootParam) {\n const byIndex = roots[parseInt(rootParam, 10)];\n const byLabel = roots.find(r => r.label === rootParam || r.path === rootParam);\n selectedRoot = byIndex || byLabel || roots[0];\n }\n\n const basePath = selectedRoot.path;\n const fullPath = resolve(basePath, reqPath.replace(/^\\//, ''));\n\n const basePathWithSep = basePath.endsWith('/') ? basePath : basePath + '/';\n if (fullPath !== basePath && !fullPath.startsWith(basePathWithSep)) {\n return { valid: false, fullPath, basePath, error: 'Access denied: path outside allowed root' };\n }\n\n const cfg = loadConfig();\n const fmCfg = (cfg as Record<string, unknown>).fileManager as { blockedPatterns?: string[] } | undefined;\n const blocked = fmCfg?.blockedPatterns || ['.ssh', '.env', '.aws', '.gnupg', 'node_modules', '.git/objects'];\n for (const pattern of blocked) {\n if (fullPath.includes(`/${pattern}`) || fullPath.endsWith(`/${pattern}`)) {\n return { valid: false, fullPath, basePath, error: `Access denied: blocked pattern \"${pattern}\"` };\n }\n }\n\n return { valid: true, fullPath, basePath };\n}\n\nexport function createFilesRouter(): Router {\n const router = Router();\n const UPLOADS_DIR = join(homedir(), '.titan', 'uploads');\n\n router.get('/roots', (_req, res) => {\n res.json({ roots: getFileRoots() });\n });\n\n router.get('/', (req, res) => {\n const reqPath = (req.query.path as string) || '';\n const rootParam = req.query.root as string | undefined;\n const { valid, fullPath, basePath, error } = validateFilePath(reqPath, rootParam);\n if (!valid) { res.status(403).json({ error }); return; }\n\n try {\n if (!fs.existsSync(fullPath)) { res.status(404).json({ error: 'Path not found' }); return; }\n const stat = fs.statSync(fullPath);\n if (!stat.isDirectory()) { res.status(400).json({ error: 'Not a directory. Use /api/files/read for files.' }); return; }\n\n const entries = fs.readdirSync(fullPath).map(name => {\n try {\n const entryPath = join(fullPath, name);\n const entryStat = fs.statSync(entryPath);\n return {\n name,\n path: reqPath ? `${reqPath}/${name}` : name,\n type: entryStat.isDirectory() ? 'directory' as const : 'file' as const,\n size: entryStat.size,\n modified: entryStat.mtime.toISOString(),\n };\n } catch {\n return { name, path: reqPath ? `${reqPath}/${name}` : name, type: 'file' as const, size: 0, modified: '' };\n }\n });\n entries.sort((a, b) => {\n if (a.type !== b.type) return a.type === 'directory' ? -1 : 1;\n return a.name.localeCompare(b.name);\n });\n res.json({ path: reqPath || '/', entries, basePath });\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.get('/read', (req, res) => {\n const reqPath = req.query.path as string;\n if (!reqPath) { res.status(400).json({ error: 'path parameter required' }); return; }\n const rootParam = req.query.root as string | undefined;\n const { valid, fullPath, error } = validateFilePath(reqPath, rootParam);\n if (!valid) { res.status(403).json({ error }); return; }\n\n try {\n if (!fs.existsSync(fullPath)) { res.status(404).json({ error: 'File not found' }); return; }\n const stat = fs.statSync(fullPath);\n if (stat.isDirectory()) { res.status(400).json({ error: 'Path is a directory' }); return; }\n\n const MAX_SIZE = 1024 * 1024;\n if (stat.size > MAX_SIZE) {\n const content = fs.readFileSync(fullPath, 'utf-8').slice(0, MAX_SIZE);\n res.json({ path: reqPath, content, truncated: true, size: stat.size, modified: stat.mtime.toISOString() });\n return;\n }\n\n const content = fs.readFileSync(fullPath, 'utf-8');\n res.json({ path: reqPath, content, truncated: false, size: stat.size, modified: stat.mtime.toISOString() });\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.post('/write', (req, res) => {\n const { path: reqPath, content, root: rootParam } = req.body as { path?: string; content?: string; root?: string };\n if (!reqPath) { res.status(400).json({ error: 'path required' }); return; }\n if (content === undefined) { res.status(400).json({ error: 'content required' }); return; }\n\n const { valid, fullPath, error } = validateFilePath(reqPath, rootParam);\n if (!valid) { res.status(403).json({ error }); return; }\n\n try {\n const dir = dirname(fullPath);\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(fullPath, content, 'utf-8');\n const stat = fs.statSync(fullPath);\n res.json({ success: true, path: reqPath, size: stat.size, modified: stat.mtime.toISOString() });\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.post('/mkdir', (req, res) => {\n const { path: reqPath, root: rootParam } = req.body as { path?: string; root?: string };\n if (!reqPath) { res.status(400).json({ error: 'path required' }); return; }\n\n const { valid, fullPath, error } = validateFilePath(reqPath, rootParam);\n if (!valid) { res.status(403).json({ error }); return; }\n\n try {\n if (fs.existsSync(fullPath)) { res.status(409).json({ error: 'Path already exists' }); return; }\n fs.mkdirSync(fullPath, { recursive: true });\n res.json({ success: true, path: reqPath });\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.post('/rename', (req, res) => {\n const { oldPath, newPath, root: rootParam } = req.body as { oldPath?: string; newPath?: string; root?: string };\n if (!oldPath || !newPath) { res.status(400).json({ error: 'oldPath and newPath required' }); return; }\n\n const oldValidation = validateFilePath(oldPath, rootParam);\n const newValidation = validateFilePath(newPath, rootParam);\n if (!oldValidation.valid) { res.status(403).json({ error: oldValidation.error }); return; }\n if (!newValidation.valid) { res.status(403).json({ error: newValidation.error }); return; }\n\n try {\n if (!fs.existsSync(oldValidation.fullPath)) { res.status(404).json({ error: 'Source not found' }); return; }\n if (fs.existsSync(newValidation.fullPath)) { res.status(409).json({ error: 'Destination already exists' }); return; }\n fs.renameSync(oldValidation.fullPath, newValidation.fullPath);\n res.json({ success: true, oldPath, newPath });\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.delete('/delete', (req, res) => {\n const reqPath = req.query.path as string;\n const rootParam = req.query.root as string | undefined;\n if (!reqPath) { res.status(400).json({ error: 'path required' }); return; }\n\n const { valid, fullPath, error } = validateFilePath(reqPath, rootParam);\n if (!valid) { res.status(403).json({ error }); return; }\n\n try {\n if (!fs.existsSync(fullPath)) { res.status(404).json({ error: 'Not found' }); return; }\n const stat = fs.statSync(fullPath);\n if (stat.isDirectory()) {\n const contents = fs.readdirSync(fullPath);\n if (contents.length > 0) { res.status(400).json({ error: 'Directory not empty. Delete contents first.' }); return; }\n fs.rmdirSync(fullPath);\n } else {\n fs.unlinkSync(fullPath);\n }\n res.json({ success: true, path: reqPath });\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n // Upload\n router.post('/upload', (req, res) => {\n try {\n const fileName = (req.headers['x-filename'] as string) || `upload-${Date.now()}`;\n const safeName = fileName.replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 100);\n const sessionId = (req.headers['x-session-id'] as string) || 'default';\n\n const body = Buffer.isBuffer(req.body) ? req.body : Buffer.from(JSON.stringify(req.body));\n if (!body || body.length === 0) { res.status(400).json({ error: 'Empty upload body' }); return; }\n\n const sessionDir = join(UPLOADS_DIR, sessionId);\n if (!fs.existsSync(sessionDir)) fs.mkdirSync(sessionDir, { recursive: true });\n\n const filePath = join(sessionDir, safeName);\n fs.writeFileSync(filePath, body);\n\n const stat = fs.statSync(filePath);\n logger.info(COMPONENT, `File uploaded: ${safeName} (${(stat.size / 1024).toFixed(0)}KB) \\u2192 session ${sessionId}`);\n\n res.json({ ok: true, file: { name: safeName, path: filePath, size: stat.size, session: sessionId, uploadedAt: new Date().toISOString() } });\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.get('/uploads', (req, res) => {\n try {\n const sessionId = (req.query.session as string) || 'default';\n const sessionDir = join(UPLOADS_DIR, sessionId);\n if (!fs.existsSync(sessionDir)) { res.json({ files: [] }); return; }\n\n const files = fs.readdirSync(sessionDir).map(name => {\n const stat = fs.statSync(join(sessionDir, name));\n return { name, size: stat.size, modified: stat.mtime.toISOString() };\n });\n res.json({ files, session: sessionId });\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.delete('/uploads/:name', (req, res) => {\n try {\n const sessionId = (req.query.session as string) || 'default';\n const filePath = join(UPLOADS_DIR, sessionId, req.params.name.replace(/[^a-zA-Z0-9._-]/g, '_'));\n if (!filePath.startsWith(UPLOADS_DIR)) { res.status(403).json({ error: 'Access denied' }); return; }\n if (fs.existsSync(filePath)) fs.unlinkSync(filePath);\n res.json({ ok: true });\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n return router;\n}\n"],"mappings":"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","names":["content"]}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Router } from "express";
|
|
3
|
+
import { loadConfig, updateConfig } from "../../config/config.js";
|
|
4
|
+
import { TITAN_VERSION } from "../../utils/constants.js";
|
|
5
|
+
import logger from "../../utils/logger.js";
|
|
6
|
+
const COMPONENT = "HardwareRouter";
|
|
7
|
+
function createHardwareRouter(broadcast) {
|
|
8
|
+
const router = Router();
|
|
9
|
+
router.get("/vram", async (_req, res) => {
|
|
10
|
+
try {
|
|
11
|
+
const { getVRAMOrchestrator } = await import("../../vram/orchestrator.js");
|
|
12
|
+
const orch = getVRAMOrchestrator();
|
|
13
|
+
const snapshot = await orch.getSnapshot();
|
|
14
|
+
if (!snapshot) {
|
|
15
|
+
res.json({ error: "GPU state unavailable" });
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
res.json(snapshot);
|
|
19
|
+
} catch (err) {
|
|
20
|
+
res.status(500).json({ error: err.message });
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
router.post("/vram/acquire", async (req, res) => {
|
|
24
|
+
try {
|
|
25
|
+
const { service, requiredMB, leaseDurationMs } = req.body;
|
|
26
|
+
if (!service || !requiredMB) {
|
|
27
|
+
res.status(400).json({ error: "service and requiredMB required" });
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (typeof requiredMB !== "number" || !Number.isFinite(requiredMB) || requiredMB <= 0) {
|
|
31
|
+
res.status(400).json({ error: "requiredMB must be a positive number" });
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const { getVRAMOrchestrator } = await import("../../vram/orchestrator.js");
|
|
35
|
+
const orch = getVRAMOrchestrator();
|
|
36
|
+
const result = await orch.acquire(service, requiredMB, leaseDurationMs || 3e5);
|
|
37
|
+
res.json(result);
|
|
38
|
+
} catch (err) {
|
|
39
|
+
res.status(500).json({ error: err.message });
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
router.post("/vram/release", async (req, res) => {
|
|
43
|
+
try {
|
|
44
|
+
const { leaseId, restoreModel } = req.body;
|
|
45
|
+
if (!leaseId) {
|
|
46
|
+
res.status(400).json({ error: "leaseId required" });
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const { getVRAMOrchestrator } = await import("../../vram/orchestrator.js");
|
|
50
|
+
const orch = getVRAMOrchestrator();
|
|
51
|
+
const result = await orch.release(leaseId, restoreModel ?? true);
|
|
52
|
+
res.json(result);
|
|
53
|
+
} catch (err) {
|
|
54
|
+
res.status(500).json({ error: err.message });
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
router.get("/vram/check", async (req, res) => {
|
|
58
|
+
try {
|
|
59
|
+
const mb = parseInt(req.query.mb, 10);
|
|
60
|
+
if (!mb || mb <= 0) {
|
|
61
|
+
res.status(400).json({ error: "mb query param required (positive integer)" });
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const { getVRAMOrchestrator } = await import("../../vram/orchestrator.js");
|
|
65
|
+
const orch = getVRAMOrchestrator();
|
|
66
|
+
const result = await orch.canAcquire(mb);
|
|
67
|
+
res.json(result);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
res.status(500).json({ error: err.message });
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
router.get("/hardware/detect", async (_req, res) => {
|
|
73
|
+
try {
|
|
74
|
+
const { detectHardware, generateRecommendations } = await import("../../hardware/autoConfig.js");
|
|
75
|
+
const profile = await detectHardware();
|
|
76
|
+
const recommendations = generateRecommendations(profile);
|
|
77
|
+
res.json({ profile, recommendations });
|
|
78
|
+
} catch (err) {
|
|
79
|
+
logger.error(COMPONENT, `Hardware detection failed: ${err.message}`);
|
|
80
|
+
res.status(500).json({ error: err.message });
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
router.post("/hardware/apply", async (_req, res) => {
|
|
84
|
+
try {
|
|
85
|
+
const { applyAutoConfiguration } = await import("../../hardware/autoConfig.js");
|
|
86
|
+
const result = await applyAutoConfiguration(false);
|
|
87
|
+
res.json({
|
|
88
|
+
success: true,
|
|
89
|
+
profile: result.profile,
|
|
90
|
+
recommendations: result.recommendations,
|
|
91
|
+
applied: result.applied
|
|
92
|
+
});
|
|
93
|
+
} catch (err) {
|
|
94
|
+
logger.error(COMPONENT, `Hardware auto-config failed: ${err.message}`);
|
|
95
|
+
res.status(500).json({ error: err.message });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
router.get("/cloud/config", (_req, res) => {
|
|
99
|
+
const isCloud = process.env.TITAN_CLOUD_MODE === "true";
|
|
100
|
+
if (!isCloud) {
|
|
101
|
+
return res.json({ cloud: false });
|
|
102
|
+
}
|
|
103
|
+
return res.json({
|
|
104
|
+
cloud: true,
|
|
105
|
+
apiUrl: process.env.TITAN_CLOUD_API || "",
|
|
106
|
+
userId: process.env.TITAN_USER_ID || "",
|
|
107
|
+
userEmail: process.env.TITAN_USER_EMAIL || ""
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
router.get("/onboarding/status", (_req, res) => {
|
|
111
|
+
const cfg = loadConfig();
|
|
112
|
+
return res.json({
|
|
113
|
+
onboarded: cfg.onboarded,
|
|
114
|
+
version: TITAN_VERSION,
|
|
115
|
+
cloud: process.env.TITAN_CLOUD_MODE === "true"
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
router.post("/onboarding/complete", (req, res) => {
|
|
119
|
+
try {
|
|
120
|
+
const { provider, apiKey, model, agentName, personality } = req.body;
|
|
121
|
+
const updates = { onboarded: true };
|
|
122
|
+
if (provider && apiKey) {
|
|
123
|
+
const providerKey = provider.toLowerCase();
|
|
124
|
+
const cfg = loadConfig();
|
|
125
|
+
const providers = { ...cfg.providers };
|
|
126
|
+
if (!providers[providerKey]) providers[providerKey] = {};
|
|
127
|
+
providers[providerKey].apiKey = apiKey;
|
|
128
|
+
updates.providers = providers;
|
|
129
|
+
}
|
|
130
|
+
if (model) {
|
|
131
|
+
updates.agent = { model };
|
|
132
|
+
}
|
|
133
|
+
if (agentName || personality) {
|
|
134
|
+
const soulParts = [];
|
|
135
|
+
if (agentName) soulParts.push(`Your name is ${agentName}.`);
|
|
136
|
+
if (personality) soulParts.push(personality);
|
|
137
|
+
updates.soul = soulParts.join(" ");
|
|
138
|
+
}
|
|
139
|
+
updateConfig(updates);
|
|
140
|
+
broadcast({ type: "config_updated" });
|
|
141
|
+
res.json({ ok: true, message: "Onboarding complete! Welcome to TITAN." });
|
|
142
|
+
} catch (e) {
|
|
143
|
+
res.status(400).json({ error: e.message });
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
return router;
|
|
147
|
+
}
|
|
148
|
+
export {
|
|
149
|
+
createHardwareRouter
|
|
150
|
+
};
|
|
151
|
+
//# sourceMappingURL=hardwareRouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/gateway/routes/hardwareRouter.ts"],"sourcesContent":["/**\n * Hardware Router\n *\n * Extracted from gateway/server.ts.\n * Consolidates VRAM, hardware detection, cloud config, and onboarding routes.\n */\n\nimport { Router } from 'express';\nimport { loadConfig, updateConfig } from '../../config/config.js';\nimport { TITAN_VERSION } from '../../utils/constants.js';\nimport logger from '../../utils/logger.js';\n\nconst COMPONENT = 'HardwareRouter';\n\nexport function createHardwareRouter(\n broadcast: (data: Record<string, unknown>, userId?: string) => void,\n): Router {\n const router = Router();\n\n // ── VRAM ────────────────────────────────────────────────────\n router.get('/vram', async (_req, res) => {\n try {\n const { getVRAMOrchestrator } = await import('../../vram/orchestrator.js');\n const orch = getVRAMOrchestrator();\n const snapshot = await orch.getSnapshot();\n if (!snapshot) {\n res.json({ error: 'GPU state unavailable' });\n return;\n }\n res.json(snapshot);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.post('/vram/acquire', async (req, res) => {\n try {\n const { service, requiredMB, leaseDurationMs } = req.body as {\n service?: string; requiredMB?: number; leaseDurationMs?: number;\n };\n if (!service || !requiredMB) {\n res.status(400).json({ error: 'service and requiredMB required' });\n return;\n }\n if (typeof requiredMB !== 'number' || !Number.isFinite(requiredMB) || requiredMB <= 0) {\n res.status(400).json({ error: 'requiredMB must be a positive number' });\n return;\n }\n const { getVRAMOrchestrator } = await import('../../vram/orchestrator.js');\n const orch = getVRAMOrchestrator();\n const result = await orch.acquire(service, requiredMB, leaseDurationMs || 300_000);\n res.json(result);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.post('/vram/release', async (req, res) => {\n try {\n const { leaseId, restoreModel } = req.body as { leaseId?: string; restoreModel?: boolean };\n if (!leaseId) {\n res.status(400).json({ error: 'leaseId required' });\n return;\n }\n const { getVRAMOrchestrator } = await import('../../vram/orchestrator.js');\n const orch = getVRAMOrchestrator();\n const result = await orch.release(leaseId, restoreModel ?? true);\n res.json(result);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.get('/vram/check', async (req, res) => {\n try {\n const mb = parseInt(req.query.mb as string, 10);\n if (!mb || mb <= 0) {\n res.status(400).json({ error: 'mb query param required (positive integer)' });\n return;\n }\n const { getVRAMOrchestrator } = await import('../../vram/orchestrator.js');\n const orch = getVRAMOrchestrator();\n const result = await orch.canAcquire(mb);\n res.json(result);\n } catch (err) {\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n // ── Hardware Detection ──────────────────────────────────────\n router.get('/hardware/detect', async (_req, res) => {\n try {\n const { detectHardware, generateRecommendations } = await import('../../hardware/autoConfig.js');\n const profile = await detectHardware();\n const recommendations = generateRecommendations(profile);\n res.json({ profile, recommendations });\n } catch (err) {\n logger.error(COMPONENT, `Hardware detection failed: ${(err as Error).message}`);\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n router.post('/hardware/apply', async (_req, res) => {\n try {\n const { applyAutoConfiguration } = await import('../../hardware/autoConfig.js');\n const result = await applyAutoConfiguration(false);\n res.json({\n success: true,\n profile: result.profile,\n recommendations: result.recommendations,\n applied: result.applied,\n });\n } catch (err) {\n logger.error(COMPONENT, `Hardware auto-config failed: ${(err as Error).message}`);\n res.status(500).json({ error: (err as Error).message });\n }\n });\n\n // ── Cloud Config ────────────────────────────────────────────\n router.get('/cloud/config', (_req, res) => {\n const isCloud = process.env.TITAN_CLOUD_MODE === 'true';\n if (!isCloud) {\n return res.json({ cloud: false });\n }\n return res.json({\n cloud: true,\n apiUrl: process.env.TITAN_CLOUD_API || '',\n userId: process.env.TITAN_USER_ID || '',\n userEmail: process.env.TITAN_USER_EMAIL || '',\n });\n });\n\n // ── Onboarding ───────────────────────────────────────────────\n router.get('/onboarding/status', (_req, res) => {\n const cfg = loadConfig();\n return res.json({\n onboarded: cfg.onboarded,\n version: TITAN_VERSION,\n cloud: process.env.TITAN_CLOUD_MODE === 'true',\n });\n });\n\n router.post('/onboarding/complete', (req, res) => {\n try {\n const { provider, apiKey, model, agentName, personality } = req.body;\n\n const updates: Record<string, unknown> = { onboarded: true };\n\n if (provider && apiKey) {\n const providerKey = provider.toLowerCase();\n const cfg = loadConfig();\n const providers = { ...cfg.providers } as Record<string, Record<string, unknown>>;\n if (!providers[providerKey]) providers[providerKey] = {};\n providers[providerKey].apiKey = apiKey;\n updates.providers = providers;\n }\n\n if (model) {\n updates.agent = { model };\n }\n\n if (agentName || personality) {\n const soulParts: string[] = [];\n if (agentName) soulParts.push(`Your name is ${agentName}.`);\n if (personality) soulParts.push(personality);\n updates.soul = soulParts.join(' ');\n }\n\n updateConfig(updates);\n broadcast({ type: 'config_updated' });\n res.json({ ok: true, message: 'Onboarding complete! Welcome to TITAN.' });\n } catch (e) {\n res.status(400).json({ error: (e as Error).message });\n }\n });\n\n return router;\n}\n"],"mappings":";AAOA,SAAS,cAAc;AACvB,SAAS,YAAY,oBAAoB;AACzC,SAAS,qBAAqB;AAC9B,OAAO,YAAY;AAEnB,MAAM,YAAY;AAEX,SAAS,qBACd,WACQ;AACR,QAAM,SAAS,OAAO;AAGtB,SAAO,IAAI,SAAS,OAAO,MAAM,QAAQ;AACvC,QAAI;AACF,YAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,4BAA4B;AACzE,YAAM,OAAO,oBAAoB;AACjC,YAAM,WAAW,MAAM,KAAK,YAAY;AACxC,UAAI,CAAC,UAAU;AACb,YAAI,KAAK,EAAE,OAAO,wBAAwB,CAAC;AAC3C;AAAA,MACF;AACA,UAAI,KAAK,QAAQ;AAAA,IACnB,SAAS,KAAK;AACZ,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAQ,IAAc,QAAQ,CAAC;AAAA,IACxD;AAAA,EACF,CAAC;AAED,SAAO,KAAK,iBAAiB,OAAO,KAAK,QAAQ;AAC/C,QAAI;AACF,YAAM,EAAE,SAAS,YAAY,gBAAgB,IAAI,IAAI;AAGrD,UAAI,CAAC,WAAW,CAAC,YAAY;AAC3B,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,kCAAkC,CAAC;AACjE;AAAA,MACF;AACA,UAAI,OAAO,eAAe,YAAY,CAAC,OAAO,SAAS,UAAU,KAAK,cAAc,GAAG;AACrF,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,uCAAuC,CAAC;AACtE;AAAA,MACF;AACA,YAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,4BAA4B;AACzE,YAAM,OAAO,oBAAoB;AACjC,YAAM,SAAS,MAAM,KAAK,QAAQ,SAAS,YAAY,mBAAmB,GAAO;AACjF,UAAI,KAAK,MAAM;AAAA,IACjB,SAAS,KAAK;AACZ,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAQ,IAAc,QAAQ,CAAC;AAAA,IACxD;AAAA,EACF,CAAC;AAED,SAAO,KAAK,iBAAiB,OAAO,KAAK,QAAQ;AAC/C,QAAI;AACF,YAAM,EAAE,SAAS,aAAa,IAAI,IAAI;AACtC,UAAI,CAAC,SAAS;AACZ,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,mBAAmB,CAAC;AAClD;AAAA,MACF;AACA,YAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,4BAA4B;AACzE,YAAM,OAAO,oBAAoB;AACjC,YAAM,SAAS,MAAM,KAAK,QAAQ,SAAS,gBAAgB,IAAI;AAC/D,UAAI,KAAK,MAAM;AAAA,IACjB,SAAS,KAAK;AACZ,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAQ,IAAc,QAAQ,CAAC;AAAA,IACxD;AAAA,EACF,CAAC;AAED,SAAO,IAAI,eAAe,OAAO,KAAK,QAAQ;AAC5C,QAAI;AACF,YAAM,KAAK,SAAS,IAAI,MAAM,IAAc,EAAE;AAC9C,UAAI,CAAC,MAAM,MAAM,GAAG;AAClB,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,6CAA6C,CAAC;AAC5E;AAAA,MACF;AACA,YAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,4BAA4B;AACzE,YAAM,OAAO,oBAAoB;AACjC,YAAM,SAAS,MAAM,KAAK,WAAW,EAAE;AACvC,UAAI,KAAK,MAAM;AAAA,IACjB,SAAS,KAAK;AACZ,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAQ,IAAc,QAAQ,CAAC;AAAA,IACxD;AAAA,EACF,CAAC;AAGD,SAAO,IAAI,oBAAoB,OAAO,MAAM,QAAQ;AAClD,QAAI;AACF,YAAM,EAAE,gBAAgB,wBAAwB,IAAI,MAAM,OAAO,8BAA8B;AAC/F,YAAM,UAAU,MAAM,eAAe;AACrC,YAAM,kBAAkB,wBAAwB,OAAO;AACvD,UAAI,KAAK,EAAE,SAAS,gBAAgB,CAAC;AAAA,IACvC,SAAS,KAAK;AACZ,aAAO,MAAM,WAAW,8BAA+B,IAAc,OAAO,EAAE;AAC9E,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAQ,IAAc,QAAQ,CAAC;AAAA,IACxD;AAAA,EACF,CAAC;AAED,SAAO,KAAK,mBAAmB,OAAO,MAAM,QAAQ;AAClD,QAAI;AACF,YAAM,EAAE,uBAAuB,IAAI,MAAM,OAAO,8BAA8B;AAC9E,YAAM,SAAS,MAAM,uBAAuB,KAAK;AACjD,UAAI,KAAK;AAAA,QACP,SAAS;AAAA,QACT,SAAS,OAAO;AAAA,QAChB,iBAAiB,OAAO;AAAA,QACxB,SAAS,OAAO;AAAA,MAClB,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,aAAO,MAAM,WAAW,gCAAiC,IAAc,OAAO,EAAE;AAChF,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAQ,IAAc,QAAQ,CAAC;AAAA,IACxD;AAAA,EACF,CAAC;AAGD,SAAO,IAAI,iBAAiB,CAAC,MAAM,QAAQ;AACzC,UAAM,UAAU,QAAQ,IAAI,qBAAqB;AACjD,QAAI,CAAC,SAAS;AACZ,aAAO,IAAI,KAAK,EAAE,OAAO,MAAM,CAAC;AAAA,IAClC;AACA,WAAO,IAAI,KAAK;AAAA,MACd,OAAO;AAAA,MACP,QAAQ,QAAQ,IAAI,mBAAmB;AAAA,MACvC,QAAQ,QAAQ,IAAI,iBAAiB;AAAA,MACrC,WAAW,QAAQ,IAAI,oBAAoB;AAAA,IAC7C,CAAC;AAAA,EACH,CAAC;AAGD,SAAO,IAAI,sBAAsB,CAAC,MAAM,QAAQ;AAC9C,UAAM,MAAM,WAAW;AACvB,WAAO,IAAI,KAAK;AAAA,MACd,WAAW,IAAI;AAAA,MACf,SAAS;AAAA,MACT,OAAO,QAAQ,IAAI,qBAAqB;AAAA,IAC1C,CAAC;AAAA,EACH,CAAC;AAED,SAAO,KAAK,wBAAwB,CAAC,KAAK,QAAQ;AAChD,QAAI;AACF,YAAM,EAAE,UAAU,QAAQ,OAAO,WAAW,YAAY,IAAI,IAAI;AAEhE,YAAM,UAAmC,EAAE,WAAW,KAAK;AAE3D,UAAI,YAAY,QAAQ;AACtB,cAAM,cAAc,SAAS,YAAY;AACzC,cAAM,MAAM,WAAW;AACvB,cAAM,YAAY,EAAE,GAAG,IAAI,UAAU;AACrC,YAAI,CAAC,UAAU,WAAW,EAAG,WAAU,WAAW,IAAI,CAAC;AACvD,kBAAU,WAAW,EAAE,SAAS;AAChC,gBAAQ,YAAY;AAAA,MACtB;AAEA,UAAI,OAAO;AACT,gBAAQ,QAAQ,EAAE,MAAM;AAAA,MAC1B;AAEA,UAAI,aAAa,aAAa;AAC5B,cAAM,YAAsB,CAAC;AAC7B,YAAI,UAAW,WAAU,KAAK,gBAAgB,SAAS,GAAG;AAC1D,YAAI,YAAa,WAAU,KAAK,WAAW;AAC3C,gBAAQ,OAAO,UAAU,KAAK,GAAG;AAAA,MACnC;AAEA,mBAAa,OAAO;AACpB,gBAAU,EAAE,MAAM,iBAAiB,CAAC;AACpC,UAAI,KAAK,EAAE,IAAI,MAAM,SAAS,yCAAyC,CAAC;AAAA,IAC1E,SAAS,GAAG;AACV,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAQ,EAAY,QAAQ,CAAC;AAAA,IACtD;AAAA,EACF,CAAC;AAED,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Router } from "express";
|
|
3
|
+
import { listMcpServers, addMcpServer, removeMcpServer, setMcpServerEnabled, getMcpStatus, BUILTIN_PRESETS } from "../../mcp/registry.js";
|
|
4
|
+
import { connectMcpServer, testMcpServer } from "../../mcp/client.js";
|
|
5
|
+
import { getMcpServerStatus } from "../../mcp/server.js";
|
|
6
|
+
function createMcpRouter() {
|
|
7
|
+
const router = Router();
|
|
8
|
+
router.get("/mcp/server", (_req, res) => {
|
|
9
|
+
res.json(getMcpServerStatus());
|
|
10
|
+
});
|
|
11
|
+
router.get("/mcp/clients", (_req, res) => {
|
|
12
|
+
const servers = listMcpServers();
|
|
13
|
+
const status = getMcpStatus();
|
|
14
|
+
const merged = servers.map((s) => {
|
|
15
|
+
const live = status.find((st) => st.server.id === s.id);
|
|
16
|
+
return { ...s, status: live?.status || "disconnected", toolCount: live?.toolCount || 0 };
|
|
17
|
+
});
|
|
18
|
+
res.json({ servers: merged });
|
|
19
|
+
});
|
|
20
|
+
router.post("/mcp/clients", async (req, res) => {
|
|
21
|
+
try {
|
|
22
|
+
const { presetId, ...serverConfig } = req.body;
|
|
23
|
+
let server;
|
|
24
|
+
if (presetId) {
|
|
25
|
+
const preset = BUILTIN_PRESETS.find((p) => p.id === presetId);
|
|
26
|
+
if (!preset) {
|
|
27
|
+
res.status(400).json({ error: `Unknown preset: ${presetId}` });
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
server = addMcpServer(preset);
|
|
31
|
+
} else {
|
|
32
|
+
server = addMcpServer(serverConfig);
|
|
33
|
+
}
|
|
34
|
+
if (server.enabled) {
|
|
35
|
+
await connectMcpServer(server).catch(() => {
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
res.json({ ok: true, server });
|
|
39
|
+
} catch (err) {
|
|
40
|
+
res.status(400).json({ error: err.message });
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
router.delete("/mcp/clients/:id", (req, res) => {
|
|
44
|
+
try {
|
|
45
|
+
removeMcpServer(req.params.id);
|
|
46
|
+
res.json({ ok: true });
|
|
47
|
+
} catch (err) {
|
|
48
|
+
res.status(400).json({ error: err.message });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
router.post("/mcp/clients/:id/toggle", (req, res) => {
|
|
52
|
+
try {
|
|
53
|
+
const { enabled } = req.body;
|
|
54
|
+
setMcpServerEnabled(req.params.id, !!enabled);
|
|
55
|
+
if (enabled) {
|
|
56
|
+
const servers = listMcpServers();
|
|
57
|
+
const server = servers.find((s) => s.id === req.params.id);
|
|
58
|
+
if (server) connectMcpServer(server).catch(() => {
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
res.json({ ok: true, enabled: !!enabled });
|
|
62
|
+
} catch (err) {
|
|
63
|
+
res.status(400).json({ error: err.message });
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
router.post("/mcp/clients/:id/test", async (req, res) => {
|
|
67
|
+
try {
|
|
68
|
+
const servers = listMcpServers();
|
|
69
|
+
const server = servers.find((s) => s.id === req.params.id);
|
|
70
|
+
if (!server) {
|
|
71
|
+
res.status(404).json({ error: "Server not found" });
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const result = await testMcpServer(server);
|
|
75
|
+
res.json(result);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
res.json({ ok: false, tools: 0, error: err.message });
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
router.get("/mcp/presets", (_req, res) => {
|
|
81
|
+
res.json({ presets: BUILTIN_PRESETS });
|
|
82
|
+
});
|
|
83
|
+
return router;
|
|
84
|
+
}
|
|
85
|
+
export {
|
|
86
|
+
createMcpRouter
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=mcpRouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/gateway/routes/mcpRouter.ts"],"sourcesContent":["/**\n * MCP Router\n *\n * Extracted from gateway/server.ts.\n * Consolidates MCP server status, client management, and presets routes.\n */\n\nimport { Router } from 'express';\nimport { listMcpServers, addMcpServer, removeMcpServer, setMcpServerEnabled, getMcpStatus, BUILTIN_PRESETS } from '../../mcp/registry.js';\nimport { connectMcpServer, testMcpServer } from '../../mcp/client.js';\nimport { getMcpServerStatus } from '../../mcp/server.js';\n\nexport function createMcpRouter(): Router {\n const router = Router();\n\n // MCP server status\n router.get('/mcp/server', (_req, res) => {\n res.json(getMcpServerStatus());\n });\n\n // MCP client management\n router.get('/mcp/clients', (_req, res) => {\n const servers = listMcpServers();\n const status = getMcpStatus();\n const merged = servers.map(s => {\n const live = status.find(st => st.server.id === s.id);\n return { ...s, status: live?.status || 'disconnected', toolCount: live?.toolCount || 0 };\n });\n res.json({ servers: merged });\n });\n\n router.post('/mcp/clients', async (req, res) => {\n try {\n const { presetId, ...serverConfig } = req.body;\n let server;\n if (presetId) {\n const preset = BUILTIN_PRESETS.find(p => p.id === presetId);\n if (!preset) { res.status(400).json({ error: `Unknown preset: ${presetId}` }); return; }\n server = addMcpServer(preset as Parameters<typeof addMcpServer>[0]);\n } else {\n server = addMcpServer(serverConfig);\n }\n if (server.enabled) {\n await connectMcpServer(server).catch(() => { /* connect errors are non-fatal */ });\n }\n res.json({ ok: true, server });\n } catch (err) {\n res.status(400).json({ error: (err as Error).message });\n }\n });\n\n router.delete('/mcp/clients/:id', (req, res) => {\n try {\n removeMcpServer(req.params.id);\n res.json({ ok: true });\n } catch (err) {\n res.status(400).json({ error: (err as Error).message });\n }\n });\n\n router.post('/mcp/clients/:id/toggle', (req, res) => {\n try {\n const { enabled } = req.body;\n setMcpServerEnabled(req.params.id, !!enabled);\n if (enabled) {\n const servers = listMcpServers();\n const server = servers.find(s => s.id === req.params.id);\n if (server) connectMcpServer(server).catch(() => {});\n }\n res.json({ ok: true, enabled: !!enabled });\n } catch (err) {\n res.status(400).json({ error: (err as Error).message });\n }\n });\n\n router.post('/mcp/clients/:id/test', async (req, res) => {\n try {\n const servers = listMcpServers();\n const server = servers.find(s => s.id === req.params.id);\n if (!server) { res.status(404).json({ error: 'Server not found' }); return; }\n const result = await testMcpServer(server);\n res.json(result);\n } catch (err) {\n res.json({ ok: false, tools: 0, error: (err as Error).message });\n }\n });\n\n router.get('/mcp/presets', (_req, res) => {\n res.json({ presets: BUILTIN_PRESETS });\n });\n\n return router;\n}\n"],"mappings":"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","names":[]}
|