neuro-cli 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +510 -0
  3. package/dist/advisor/advisor.d.ts +50 -0
  4. package/dist/advisor/advisor.js +178 -0
  5. package/dist/agents/base.d.ts +62 -0
  6. package/dist/agents/base.js +215 -0
  7. package/dist/agents/orchestrator.d.ts +46 -0
  8. package/dist/agents/orchestrator.js +192 -0
  9. package/dist/agents/team.d.ts +51 -0
  10. package/dist/agents/team.js +210 -0
  11. package/dist/api/models.d.ts +23 -0
  12. package/dist/api/models.js +514 -0
  13. package/dist/api/ollama.d.ts +153 -0
  14. package/dist/api/ollama.js +751 -0
  15. package/dist/api/openrouter.d.ts +55 -0
  16. package/dist/api/openrouter.js +223 -0
  17. package/dist/commands/commands.d.ts +43 -0
  18. package/dist/commands/commands.js +308 -0
  19. package/dist/config/config.d.ts +8 -0
  20. package/dist/config/config.js +311 -0
  21. package/dist/context/compaction.d.ts +54 -0
  22. package/dist/context/compaction.js +251 -0
  23. package/dist/context/custom-agents.d.ts +107 -0
  24. package/dist/context/custom-agents.js +397 -0
  25. package/dist/context/custom-tools.d.ts +120 -0
  26. package/dist/context/custom-tools.js +564 -0
  27. package/dist/context/git-checkpoint.d.ts +52 -0
  28. package/dist/context/git-checkpoint.js +240 -0
  29. package/dist/context/neuro-md.d.ts +48 -0
  30. package/dist/context/neuro-md.js +202 -0
  31. package/dist/context/neuroignore.d.ts +102 -0
  32. package/dist/context/neuroignore.js +441 -0
  33. package/dist/context/repo-map.d.ts +38 -0
  34. package/dist/context/repo-map.js +220 -0
  35. package/dist/context/skill-standard.d.ts +262 -0
  36. package/dist/context/skill-standard.js +1156 -0
  37. package/dist/context/skill-system.d.ts +75 -0
  38. package/dist/context/skill-system.js +578 -0
  39. package/dist/context/tree-sitter.d.ts +99 -0
  40. package/dist/context/tree-sitter.js +1956 -0
  41. package/dist/core/acp.d.ts +325 -0
  42. package/dist/core/acp.js +1498 -0
  43. package/dist/core/api-server.d.ts +143 -0
  44. package/dist/core/api-server.js +550 -0
  45. package/dist/core/approval.d.ts +81 -0
  46. package/dist/core/approval.js +432 -0
  47. package/dist/core/auto-compact.d.ts +127 -0
  48. package/dist/core/auto-compact.js +436 -0
  49. package/dist/core/auto-mode.d.ts +232 -0
  50. package/dist/core/auto-mode.js +831 -0
  51. package/dist/core/background-session.d.ts +166 -0
  52. package/dist/core/background-session.js +696 -0
  53. package/dist/core/cicd.d.ts +257 -0
  54. package/dist/core/cicd.js +1443 -0
  55. package/dist/core/cloud-sync.d.ts +156 -0
  56. package/dist/core/cloud-sync.js +582 -0
  57. package/dist/core/code-review.d.ts +132 -0
  58. package/dist/core/code-review.js +1191 -0
  59. package/dist/core/completion.d.ts +49 -0
  60. package/dist/core/completion.js +384 -0
  61. package/dist/core/context.d.ts +38 -0
  62. package/dist/core/context.js +144 -0
  63. package/dist/core/diff-preview.d.ts +35 -0
  64. package/dist/core/diff-preview.js +173 -0
  65. package/dist/core/doom-loop.d.ts +51 -0
  66. package/dist/core/doom-loop.js +179 -0
  67. package/dist/core/engine.d.ts +183 -0
  68. package/dist/core/engine.js +942 -0
  69. package/dist/core/extended-thinking.d.ts +103 -0
  70. package/dist/core/extended-thinking.js +269 -0
  71. package/dist/core/fallback.d.ts +54 -0
  72. package/dist/core/fallback.js +104 -0
  73. package/dist/core/git-worktree.d.ts +139 -0
  74. package/dist/core/git-worktree.js +614 -0
  75. package/dist/core/headless.d.ts +30 -0
  76. package/dist/core/headless.js +101 -0
  77. package/dist/core/i18n.d.ts +83 -0
  78. package/dist/core/i18n.js +739 -0
  79. package/dist/core/linting.d.ts +129 -0
  80. package/dist/core/linting.js +699 -0
  81. package/dist/core/model-router.d.ts +109 -0
  82. package/dist/core/model-router.js +581 -0
  83. package/dist/core/multi-model.d.ts +243 -0
  84. package/dist/core/multi-model.js +1099 -0
  85. package/dist/core/multi-session.d.ts +144 -0
  86. package/dist/core/multi-session.js +442 -0
  87. package/dist/core/multimodal.d.ts +125 -0
  88. package/dist/core/multimodal.js +286 -0
  89. package/dist/core/observability.d.ts +93 -0
  90. package/dist/core/observability.js +737 -0
  91. package/dist/core/os-sandbox.d.ts +122 -0
  92. package/dist/core/os-sandbox.js +1193 -0
  93. package/dist/core/outcome-grading.d.ts +228 -0
  94. package/dist/core/outcome-grading.js +1123 -0
  95. package/dist/core/output-styles.d.ts +57 -0
  96. package/dist/core/output-styles.js +382 -0
  97. package/dist/core/parallel-agents.d.ts +183 -0
  98. package/dist/core/parallel-agents.js +563 -0
  99. package/dist/core/plugin-bundle.d.ts +236 -0
  100. package/dist/core/plugin-bundle.js +887 -0
  101. package/dist/core/plugin-sdk.d.ts +139 -0
  102. package/dist/core/plugin-sdk.js +273 -0
  103. package/dist/core/prompt-cache.d.ts +163 -0
  104. package/dist/core/prompt-cache.js +599 -0
  105. package/dist/core/sandbox.d.ts +127 -0
  106. package/dist/core/sandbox.js +369 -0
  107. package/dist/core/scheduled-tasks.d.ts +151 -0
  108. package/dist/core/scheduled-tasks.js +502 -0
  109. package/dist/core/security-scanner.d.ts +160 -0
  110. package/dist/core/security-scanner.js +1494 -0
  111. package/dist/core/session.d.ts +83 -0
  112. package/dist/core/session.js +269 -0
  113. package/dist/core/shell-completion.d.ts +51 -0
  114. package/dist/core/shell-completion.js +674 -0
  115. package/dist/core/smart-monitor.d.ts +146 -0
  116. package/dist/core/smart-monitor.js +1199 -0
  117. package/dist/core/spec-driven.d.ts +233 -0
  118. package/dist/core/spec-driven.js +1485 -0
  119. package/dist/core/spending-warnings.d.ts +123 -0
  120. package/dist/core/spending-warnings.js +456 -0
  121. package/dist/core/sub-agent.d.ts +298 -0
  122. package/dist/core/sub-agent.js +1023 -0
  123. package/dist/core/telemetry.d.ts +157 -0
  124. package/dist/core/telemetry.js +412 -0
  125. package/dist/core/terminal-ux.d.ts +134 -0
  126. package/dist/core/terminal-ux.js +649 -0
  127. package/dist/core/testing.d.ts +146 -0
  128. package/dist/core/testing.js +1200 -0
  129. package/dist/core/types.d.ts +439 -0
  130. package/dist/core/types.js +6 -0
  131. package/dist/core/undo-redo.d.ts +112 -0
  132. package/dist/core/undo-redo.js +290 -0
  133. package/dist/core/updater.d.ts +159 -0
  134. package/dist/core/updater.js +608 -0
  135. package/dist/core/vim-mode.d.ts +151 -0
  136. package/dist/core/vim-mode.js +771 -0
  137. package/dist/core/voice.d.ts +137 -0
  138. package/dist/core/voice.js +538 -0
  139. package/dist/core/web-dashboard.d.ts +109 -0
  140. package/dist/core/web-dashboard.js +484 -0
  141. package/dist/hooks/hooks.d.ts +74 -0
  142. package/dist/hooks/hooks.js +160 -0
  143. package/dist/hooks/llm-evaluator.d.ts +165 -0
  144. package/dist/hooks/llm-evaluator.js +560 -0
  145. package/dist/index.d.ts +3 -0
  146. package/dist/index.js +1186 -0
  147. package/dist/lsp/lsp-manager.d.ts +63 -0
  148. package/dist/lsp/lsp-manager.js +351 -0
  149. package/dist/mcp/client.d.ts +133 -0
  150. package/dist/mcp/client.js +684 -0
  151. package/dist/mcp/mcp-apps.d.ts +70 -0
  152. package/dist/mcp/mcp-apps.js +1007 -0
  153. package/dist/tools/bash.d.ts +5 -0
  154. package/dist/tools/bash.js +195 -0
  155. package/dist/tools/browser.d.ts +92 -0
  156. package/dist/tools/browser.js +1570 -0
  157. package/dist/tools/extended.d.ts +6 -0
  158. package/dist/tools/extended.js +191 -0
  159. package/dist/tools/file.d.ts +10 -0
  160. package/dist/tools/file.js +382 -0
  161. package/dist/tools/github.d.ts +389 -0
  162. package/dist/tools/github.js +759 -0
  163. package/dist/tools/index.d.ts +9 -0
  164. package/dist/tools/index.js +40 -0
  165. package/dist/tools/memory.d.ts +6 -0
  166. package/dist/tools/memory.js +197 -0
  167. package/dist/tools/registry.d.ts +29 -0
  168. package/dist/tools/registry.js +64 -0
  169. package/dist/tools/web.d.ts +6 -0
  170. package/dist/tools/web.js +150 -0
  171. package/dist/ui/renderer.d.ts +97 -0
  172. package/dist/ui/renderer.js +279 -0
  173. package/dist/ui/theme.d.ts +27 -0
  174. package/dist/ui/theme.js +106 -0
  175. package/package.json +83 -0
@@ -0,0 +1,109 @@
1
+ export interface DashboardConfig {
2
+ /** Host to bind to */
3
+ host: string;
4
+ /** Port for the dashboard server */
5
+ port: number;
6
+ /** Whether to auto-open browser */
7
+ autoOpen: boolean;
8
+ /** Refresh interval for real-time updates in ms */
9
+ refreshInterval: number;
10
+ /** Whether to enable the dashboard */
11
+ enabled: boolean;
12
+ }
13
+ export interface DashboardData {
14
+ sessions: SessionSummary[];
15
+ tokenUsage: TokenUsageChart;
16
+ modelPerformance: ModelPerfData[];
17
+ spending: SpendingData;
18
+ systemInfo: SystemInfoData;
19
+ }
20
+ export interface SessionSummary {
21
+ id: string;
22
+ createdAt: number;
23
+ updatedAt: number;
24
+ messageCount: number;
25
+ model: string;
26
+ totalCost: number;
27
+ description?: string;
28
+ tags: string[];
29
+ }
30
+ export interface TokenUsageChart {
31
+ labels: string[];
32
+ inputTokens: number[];
33
+ outputTokens: number[];
34
+ costs: number[];
35
+ }
36
+ export interface ModelPerfData {
37
+ model: string;
38
+ requestCount: number;
39
+ totalTokens: number;
40
+ totalCost: number;
41
+ avgLatencyMs: number;
42
+ }
43
+ export interface SpendingData {
44
+ todayTotal: number;
45
+ todayByModel: Record<string, number>;
46
+ sessionTotal: number;
47
+ sessionByModel: Record<string, number>;
48
+ dailyLimit: number;
49
+ estimatedDailySpend: number;
50
+ }
51
+ export interface SystemInfoData {
52
+ version: string;
53
+ uptime: number;
54
+ platform: string;
55
+ nodeVersion: string;
56
+ modelsAvailable: number;
57
+ toolsAvailable: number;
58
+ agentsAvailable: number;
59
+ currentModel: string;
60
+ }
61
+ export declare class WebDashboard {
62
+ private config;
63
+ private server;
64
+ private isRunning;
65
+ private startTime;
66
+ private engineRef;
67
+ constructor(config?: Partial<DashboardConfig>);
68
+ /**
69
+ * Set the engine reference for fetching data
70
+ */
71
+ setEngine(engine: unknown): void;
72
+ /**
73
+ * Start the dashboard server
74
+ */
75
+ start(): Promise<void>;
76
+ /**
77
+ * Stop the dashboard server
78
+ */
79
+ stop(): Promise<void>;
80
+ /**
81
+ * Check if dashboard is running
82
+ */
83
+ getIsRunning(): boolean;
84
+ /**
85
+ * Get dashboard URL
86
+ */
87
+ getUrl(): string;
88
+ /**
89
+ * Gather dashboard data
90
+ */
91
+ gatherData(): DashboardData;
92
+ /**
93
+ * Get config
94
+ */
95
+ getConfig(): DashboardConfig;
96
+ /**
97
+ * Print dashboard status
98
+ */
99
+ printStatus(): void;
100
+ private handleRequest;
101
+ private getSessionSummaries;
102
+ private getTokenUsageData;
103
+ private getModelPerformanceData;
104
+ private getSpendingData;
105
+ private getSystemInfo;
106
+ private saveConfig;
107
+ private loadConfig;
108
+ }
109
+ //# sourceMappingURL=web-dashboard.d.ts.map
@@ -0,0 +1,484 @@
1
+ // ============================================================
2
+ // NeuroCLI - Web Dashboard
3
+ // Local web server serving a dashboard page
4
+ // Session history, token usage charts, model performance
5
+ // Real-time session monitoring
6
+ // ============================================================
7
+ import { createServer } from 'http';
8
+ import chalk from 'chalk';
9
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'fs';
10
+ import { join } from 'path';
11
+ import { homedir } from 'os';
12
+ // -----------------------------------------------------------
13
+ // Default config
14
+ // -----------------------------------------------------------
15
+ const DASHBOARD_CONFIG_PATH = join(homedir(), '.neuro', 'dashboard-config.json');
16
+ function defaultConfig() {
17
+ return {
18
+ host: '127.0.0.1',
19
+ port: 3142,
20
+ autoOpen: true,
21
+ refreshInterval: 5000,
22
+ enabled: false,
23
+ };
24
+ }
25
+ // -----------------------------------------------------------
26
+ // Dashboard HTML Generator
27
+ // Uses string concatenation to avoid nested template literal issues
28
+ // -----------------------------------------------------------
29
+ function generateDashboardHTML(data, config) {
30
+ const CSS = [
31
+ '* { margin: 0; padding: 0; box-sizing: border-box; }',
32
+ 'body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0d1117; color: #c9d1d9; min-height: 100vh; }',
33
+ '.header { background: linear-gradient(135deg, #161b22, #21262d); padding: 20px 30px; border-bottom: 1px solid #30363d; display: flex; align-items: center; justify-content: space-between; }',
34
+ '.header h1 { font-size: 24px; color: #58a6ff; }',
35
+ '.header .version { color: #8b949e; font-size: 14px; }',
36
+ '.header .status { display: flex; align-items: center; gap: 8px; }',
37
+ '.status-dot { width: 8px; height: 8px; border-radius: 50%; background: #3fb950; animation: pulse 2s ease-in-out infinite; }',
38
+ '@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }',
39
+ '.container { max-width: 1400px; margin: 0 auto; padding: 20px; display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 20px; }',
40
+ '.card { background: #161b22; border: 1px solid #30363d; border-radius: 12px; padding: 20px; }',
41
+ '.card h2 { color: #58a6ff; font-size: 16px; margin-bottom: 16px; }',
42
+ '.stat-row { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid #21262d; }',
43
+ '.stat-row:last-child { border-bottom: none; }',
44
+ '.stat-label { color: #8b949e; }',
45
+ '.stat-value { color: #f0f6fc; font-weight: 600; }',
46
+ '.stat-value.green { color: #3fb950; }',
47
+ '.stat-value.yellow { color: #d29922; }',
48
+ '.stat-value.cyan { color: #58a6ff; }',
49
+ '.model-row { display: flex; justify-content: space-between; align-items: center; padding: 10px; margin: 4px 0; background: #0d1117; border-radius: 8px; }',
50
+ '.model-name { color: #79c0ff; font-size: 13px; max-width: 200px; overflow: hidden; text-overflow: ellipsis; }',
51
+ '.model-stats { display: flex; gap: 16px; font-size: 12px; }',
52
+ '.session-item { padding: 10px; margin: 4px 0; background: #0d1117; border-radius: 8px; cursor: pointer; }',
53
+ '.session-item:hover { background: #21262d; }',
54
+ '.session-id { color: #79c0ff; font-size: 13px; }',
55
+ '.session-meta { color: #8b949e; font-size: 12px; margin-top: 4px; }',
56
+ '.full-width { grid-column: 1 / -1; }',
57
+ '.refresh-info { color: #8b949e; font-size: 12px; text-align: center; padding: 10px; }',
58
+ '.empty-state { color: #8b949e; text-align: center; padding: 20px; }',
59
+ ].join('\n ');
60
+ // Build model performance section
61
+ const modelRows = data.modelPerformance.length > 0
62
+ ? data.modelPerformance.map(m => '<div class="model-row">' +
63
+ '<span class="model-name">' + m.model + '</span>' +
64
+ '<div class="model-stats">' +
65
+ '<span>' + m.requestCount + ' reqs</span>' +
66
+ '<span>' + m.totalTokens.toLocaleString() + ' tokens</span>' +
67
+ '<span>$' + m.totalCost.toFixed(4) + '</span>' +
68
+ '<span>' + m.avgLatencyMs + 'ms avg</span>' +
69
+ '</div></div>').join('\n ')
70
+ : '<div class="empty-state">No model performance data yet</div>';
71
+ // Build session list
72
+ const sessionItems = data.sessions.length > 0
73
+ ? data.sessions.slice(0, 10).map(s => '<div class="session-item">' +
74
+ '<div class="session-id">' + s.id.slice(0, 8) + '...</div>' +
75
+ '<div class="session-meta">' +
76
+ s.messageCount + ' messages | ' + (s.model.split('/').pop() || s.model) + ' | $' + s.totalCost.toFixed(4) + ' | ' +
77
+ new Date(s.updatedAt).toLocaleString() +
78
+ '</div></div>').join('\n ')
79
+ : '<div class="empty-state">No sessions yet</div>';
80
+ // Build token usage section
81
+ const totalInput = data.tokenUsage.inputTokens.reduce((a, b) => a + b, 0);
82
+ const totalOutput = data.tokenUsage.outputTokens.reduce((a, b) => a + b, 0);
83
+ const totalCost = data.tokenUsage.costs.reduce((a, b) => a + b, 0);
84
+ const chartSection = data.tokenUsage.labels.length > 0
85
+ ? '<div class="bar-chart">' +
86
+ data.tokenUsage.inputTokens.map((val, i) => {
87
+ const maxVal = Math.max(...data.tokenUsage.inputTokens, ...data.tokenUsage.outputTokens, 1);
88
+ const height = Math.max(5, (val / maxVal) * 100);
89
+ return '<div class="bar" style="height: ' + height + '%">' +
90
+ '<div class="bar-label">' + data.tokenUsage.labels[i] + '</div>' +
91
+ '</div>';
92
+ }).join('') +
93
+ '</div>'
94
+ : '<div class="empty-state">No token usage data yet</div>';
95
+ const dailyLimitStr = data.spending.dailyLimit > 0
96
+ ? '$' + data.spending.dailyLimit.toFixed(2)
97
+ : 'Unlimited';
98
+ const refreshSec = String(config.refreshInterval / 1000);
99
+ return [
100
+ '<!DOCTYPE html>',
101
+ '<html lang="en">',
102
+ '<head>',
103
+ '<meta charset="UTF-8">',
104
+ '<meta name="viewport" content="width=device-width, initial-scale=1.0">',
105
+ '<title>NeuroCLI Dashboard</title>',
106
+ '<style>',
107
+ ' ' + CSS,
108
+ '</style>',
109
+ '</head>',
110
+ '<body>',
111
+ ' <div class="header">',
112
+ ' <div>',
113
+ ' <h1>NeuroCLI Dashboard</h1>',
114
+ ' <div class="version">v' + data.systemInfo.version + ' | ' + data.systemInfo.platform + ' | Node ' + data.systemInfo.nodeVersion + '</div>',
115
+ ' </div>',
116
+ ' <div class="status">',
117
+ ' <div class="status-dot"></div>',
118
+ ' <span>Live</span>',
119
+ ' </div>',
120
+ ' </div>',
121
+ '',
122
+ ' <div class="container">',
123
+ ' <!-- System Overview -->',
124
+ ' <div class="card">',
125
+ ' <h2>&#9881; System Overview</h2>',
126
+ ' <div class="stat-row"><span class="stat-label">Current Model</span><span class="stat-value cyan">' + (data.systemInfo.currentModel || 'default') + '</span></div>',
127
+ ' <div class="stat-row"><span class="stat-label">Models Available</span><span class="stat-value">' + data.systemInfo.modelsAvailable + '</span></div>',
128
+ ' <div class="stat-row"><span class="stat-label">Tools Available</span><span class="stat-value">' + data.systemInfo.toolsAvailable + '</span></div>',
129
+ ' <div class="stat-row"><span class="stat-label">Agents Available</span><span class="stat-value">' + data.systemInfo.agentsAvailable + '</span></div>',
130
+ ' <div class="stat-row"><span class="stat-label">Uptime</span><span class="stat-value">' + Math.round(data.systemInfo.uptime / 1000) + 's</span></div>',
131
+ ' </div>',
132
+ '',
133
+ ' <!-- Spending -->',
134
+ ' <div class="card">',
135
+ ' <h2>&#128176; Spending</h2>',
136
+ ' <div class="stat-row"><span class="stat-label">Today\'s Total</span><span class="stat-value green">$' + data.spending.todayTotal.toFixed(4) + '</span></div>',
137
+ ' <div class="stat-row"><span class="stat-label">Session Total</span><span class="stat-value cyan">$' + data.spending.sessionTotal.toFixed(4) + '</span></div>',
138
+ ' <div class="stat-row"><span class="stat-label">Daily Limit</span><span class="stat-value">' + dailyLimitStr + '</span></div>',
139
+ ' <div class="stat-row"><span class="stat-label">Est. Daily Spend</span><span class="stat-value yellow">$' + data.spending.estimatedDailySpend.toFixed(4) + '</span></div>',
140
+ ' </div>',
141
+ '',
142
+ ' <!-- Token Usage -->',
143
+ ' <div class="card full-width">',
144
+ ' <h2>&#128200; Token Usage</h2>',
145
+ ' ' + chartSection,
146
+ ' <div class="stat-row"><span class="stat-label">Total Input Tokens</span><span class="stat-value">' + totalInput.toLocaleString() + '</span></div>',
147
+ ' <div class="stat-row"><span class="stat-label">Total Output Tokens</span><span class="stat-value">' + totalOutput.toLocaleString() + '</span></div>',
148
+ ' <div class="stat-row"><span class="stat-label">Total Cost</span><span class="stat-value green">$' + totalCost.toFixed(4) + '</span></div>',
149
+ ' </div>',
150
+ '',
151
+ ' <!-- Model Performance -->',
152
+ ' <div class="card">',
153
+ ' <h2>&#9889; Model Performance</h2>',
154
+ ' ' + modelRows,
155
+ ' </div>',
156
+ '',
157
+ ' <!-- Session History -->',
158
+ ' <div class="card">',
159
+ ' <h2>&#128193; Session History</h2>',
160
+ ' ' + sessionItems,
161
+ ' </div>',
162
+ ' </div>',
163
+ '',
164
+ ' <div class="refresh-info">',
165
+ ' Auto-refreshing every ' + refreshSec + 's |',
166
+ ' <a href="/api/data" style="color: #58a6ff;">API Endpoint</a> |',
167
+ ' <a href="/api/docs" style="color: #58a6ff;">API Docs</a>',
168
+ ' </div>',
169
+ '',
170
+ ' <script>',
171
+ ' setTimeout(function() { location.reload(); }, ' + config.refreshInterval + ');',
172
+ ' </script>',
173
+ '</body>',
174
+ '</html>',
175
+ ].join('\n');
176
+ }
177
+ // -----------------------------------------------------------
178
+ // WebDashboard
179
+ // -----------------------------------------------------------
180
+ export class WebDashboard {
181
+ config;
182
+ server = null;
183
+ isRunning = false;
184
+ startTime = 0;
185
+ engineRef = null;
186
+ constructor(config) {
187
+ this.config = { ...defaultConfig(), ...config };
188
+ this.loadConfig();
189
+ }
190
+ // ----------------------------------------------------------
191
+ // Public API
192
+ // ----------------------------------------------------------
193
+ /**
194
+ * Set the engine reference for fetching data
195
+ */
196
+ setEngine(engine) {
197
+ this.engineRef = engine;
198
+ }
199
+ /**
200
+ * Start the dashboard server
201
+ */
202
+ async start() {
203
+ if (this.isRunning) {
204
+ console.log(chalk.yellow('Dashboard is already running.'));
205
+ return;
206
+ }
207
+ return new Promise((resolve, reject) => {
208
+ this.server = createServer(async (req, res) => {
209
+ await this.handleRequest(req, res);
210
+ });
211
+ this.server.on('error', (err) => {
212
+ console.log(chalk.red('Dashboard error: ' + err.message));
213
+ reject(err);
214
+ });
215
+ this.server.listen(this.config.port, this.config.host, () => {
216
+ this.isRunning = true;
217
+ this.startTime = Date.now();
218
+ const url = 'http://' + this.config.host + ':' + this.config.port;
219
+ console.log(chalk.green('Dashboard running at ' + url));
220
+ // Auto-open browser
221
+ if (this.config.autoOpen) {
222
+ try {
223
+ // @ts-ignore
224
+ import('open').then(mod => {
225
+ mod.default(url).catch(() => { });
226
+ }).catch(() => {
227
+ console.log(chalk.gray('Open ' + url + ' in your browser to view the dashboard.'));
228
+ });
229
+ }
230
+ catch {
231
+ console.log(chalk.gray('Open ' + url + ' in your browser to view the dashboard.'));
232
+ }
233
+ }
234
+ resolve();
235
+ });
236
+ });
237
+ }
238
+ /**
239
+ * Stop the dashboard server
240
+ */
241
+ async stop() {
242
+ if (!this.isRunning || !this.server)
243
+ return;
244
+ return new Promise((resolve) => {
245
+ this.server.close(() => {
246
+ this.isRunning = false;
247
+ this.server = null;
248
+ console.log(chalk.gray('Dashboard stopped.'));
249
+ resolve();
250
+ });
251
+ });
252
+ }
253
+ /**
254
+ * Check if dashboard is running
255
+ */
256
+ getIsRunning() {
257
+ return this.isRunning;
258
+ }
259
+ /**
260
+ * Get dashboard URL
261
+ */
262
+ getUrl() {
263
+ return 'http://' + this.config.host + ':' + this.config.port;
264
+ }
265
+ /**
266
+ * Gather dashboard data
267
+ */
268
+ gatherData() {
269
+ const sessions = this.getSessionSummaries();
270
+ const tokenUsage = this.getTokenUsageData(sessions);
271
+ const modelPerformance = this.getModelPerformanceData();
272
+ const spending = this.getSpendingData();
273
+ const systemInfo = this.getSystemInfo();
274
+ return {
275
+ sessions,
276
+ tokenUsage,
277
+ modelPerformance,
278
+ spending,
279
+ systemInfo,
280
+ };
281
+ }
282
+ /**
283
+ * Get config
284
+ */
285
+ getConfig() {
286
+ return { ...this.config };
287
+ }
288
+ /**
289
+ * Print dashboard status
290
+ */
291
+ printStatus() {
292
+ console.log('');
293
+ console.log(chalk.bold('--- NeuroCLI Web Dashboard ---'));
294
+ console.log(' Running: ' + (this.isRunning ? chalk.green('yes') : chalk.gray('no')));
295
+ if (this.isRunning) {
296
+ console.log(' URL: ' + chalk.cyan(this.getUrl()));
297
+ console.log(' Uptime: ' + Math.round((Date.now() - this.startTime) / 1000) + 's');
298
+ }
299
+ console.log(' Auto-open: ' + (this.config.autoOpen ? chalk.green('yes') : chalk.gray('no')));
300
+ console.log(' Refresh interval: ' + (this.config.refreshInterval / 1000) + 's');
301
+ console.log('');
302
+ }
303
+ // ----------------------------------------------------------
304
+ // Private helpers
305
+ // ----------------------------------------------------------
306
+ async handleRequest(req, res) {
307
+ const url = req.url || '/';
308
+ // CORS
309
+ res.setHeader('Access-Control-Allow-Origin', '*');
310
+ switch (url) {
311
+ case '/':
312
+ case '/dashboard': {
313
+ const data = this.gatherData();
314
+ const html = generateDashboardHTML(data, this.config);
315
+ res.writeHead(200, { 'Content-Type': 'text/html' });
316
+ res.end(html);
317
+ break;
318
+ }
319
+ case '/api/data': {
320
+ const data = this.gatherData();
321
+ res.writeHead(200, { 'Content-Type': 'application/json' });
322
+ res.end(JSON.stringify(data));
323
+ break;
324
+ }
325
+ case '/api/sessions': {
326
+ const sessions = this.getSessionSummaries();
327
+ res.writeHead(200, { 'Content-Type': 'application/json' });
328
+ res.end(JSON.stringify({ sessions }));
329
+ break;
330
+ }
331
+ case '/api/docs': {
332
+ res.writeHead(200, { 'Content-Type': 'application/json' });
333
+ res.end(JSON.stringify({
334
+ endpoints: [
335
+ { method: 'GET', path: '/', description: 'Dashboard HTML page' },
336
+ { method: 'GET', path: '/api/data', description: 'All dashboard data' },
337
+ { method: 'GET', path: '/api/sessions', description: 'Session summaries' },
338
+ { method: 'GET', path: '/api/docs', description: 'This documentation' },
339
+ ],
340
+ }));
341
+ break;
342
+ }
343
+ default: {
344
+ res.writeHead(404, { 'Content-Type': 'application/json' });
345
+ res.end(JSON.stringify({ error: 'Not found' }));
346
+ }
347
+ }
348
+ }
349
+ getSessionSummaries() {
350
+ const sessionDir = join(homedir(), '.neuro', 'sessions');
351
+ const sessions = [];
352
+ try {
353
+ if (!existsSync(sessionDir))
354
+ return sessions;
355
+ const files = readdirSync(sessionDir).filter(f => f.endsWith('.json'));
356
+ for (const file of files) {
357
+ try {
358
+ const raw = readFileSync(join(sessionDir, file), 'utf-8');
359
+ const data = JSON.parse(raw);
360
+ sessions.push({
361
+ id: data.id || file.replace('.json', ''),
362
+ createdAt: data.createdAt || 0,
363
+ updatedAt: data.updatedAt || 0,
364
+ messageCount: data.messages?.length || 0,
365
+ model: data.model || 'unknown',
366
+ totalCost: data.totalCost || 0,
367
+ description: data.description,
368
+ tags: data.tags || [],
369
+ });
370
+ }
371
+ catch { /* skip invalid */ }
372
+ }
373
+ // Sort by updatedAt descending
374
+ sessions.sort((a, b) => b.updatedAt - a.updatedAt);
375
+ }
376
+ catch { /* ignore */ }
377
+ return sessions;
378
+ }
379
+ getTokenUsageData(sessions) {
380
+ const byDate = {};
381
+ for (const session of sessions) {
382
+ const date = new Date(session.updatedAt).toLocaleDateString();
383
+ if (!byDate[date])
384
+ byDate[date] = { input: 0, output: 0, cost: 0 };
385
+ byDate[date].cost += session.totalCost;
386
+ }
387
+ const labels = Object.keys(byDate).slice(-7);
388
+ return {
389
+ labels,
390
+ inputTokens: labels.map(l => byDate[l]?.input || 0),
391
+ outputTokens: labels.map(l => byDate[l]?.output || 0),
392
+ costs: labels.map(l => byDate[l]?.cost || 0),
393
+ };
394
+ }
395
+ getModelPerformanceData() {
396
+ const telemetryPath = join(homedir(), '.neuro', 'telemetry', 'models.json');
397
+ try {
398
+ if (existsSync(telemetryPath)) {
399
+ const raw = readFileSync(telemetryPath, 'utf-8');
400
+ const items = JSON.parse(raw);
401
+ return items.map(item => ({
402
+ model: item.key,
403
+ requestCount: item.requests,
404
+ totalTokens: item.inputTokens + item.outputTokens,
405
+ totalCost: item.cost,
406
+ avgLatencyMs: item.latencies.length > 0
407
+ ? Math.round(item.latencies.reduce((a, b) => a + b, 0) / item.latencies.length)
408
+ : 0,
409
+ }));
410
+ }
411
+ }
412
+ catch { /* ignore */ }
413
+ return [];
414
+ }
415
+ getSpendingData() {
416
+ const spendingPath = join(homedir(), '.neuro', 'spending');
417
+ const today = new Date();
418
+ const dateStr = today.getFullYear() + '-' +
419
+ (today.getMonth() + 1).toString().padStart(2, '0') + '-' +
420
+ today.getDate().toString().padStart(2, '0');
421
+ try {
422
+ const filePath = join(spendingPath, dateStr + '.json');
423
+ if (existsSync(filePath)) {
424
+ const raw = readFileSync(filePath, 'utf-8');
425
+ const entries = JSON.parse(raw);
426
+ let todayTotal = 0;
427
+ const todayByModel = {};
428
+ for (const entry of entries) {
429
+ todayTotal += entry.cost;
430
+ todayByModel[entry.model] = (todayByModel[entry.model] || 0) + entry.cost;
431
+ }
432
+ return {
433
+ todayTotal,
434
+ todayByModel,
435
+ sessionTotal: todayTotal,
436
+ sessionByModel: todayByModel,
437
+ dailyLimit: 0,
438
+ estimatedDailySpend: todayTotal,
439
+ };
440
+ }
441
+ }
442
+ catch { /* ignore */ }
443
+ return {
444
+ todayTotal: 0,
445
+ todayByModel: {},
446
+ sessionTotal: 0,
447
+ sessionByModel: {},
448
+ dailyLimit: 0,
449
+ estimatedDailySpend: 0,
450
+ };
451
+ }
452
+ getSystemInfo() {
453
+ return {
454
+ version: '3.0.0',
455
+ uptime: this.isRunning ? Date.now() - this.startTime : 0,
456
+ platform: process.platform,
457
+ nodeVersion: process.version,
458
+ modelsAvailable: 23,
459
+ toolsAvailable: 10,
460
+ agentsAvailable: 8,
461
+ currentModel: 'default',
462
+ };
463
+ }
464
+ saveConfig() {
465
+ try {
466
+ const dir = join(DASHBOARD_CONFIG_PATH, '..');
467
+ if (!existsSync(dir))
468
+ mkdirSync(dir, { recursive: true });
469
+ writeFileSync(DASHBOARD_CONFIG_PATH, JSON.stringify(this.config, null, 2), 'utf-8');
470
+ }
471
+ catch { /* Silently fail */ }
472
+ }
473
+ loadConfig() {
474
+ try {
475
+ if (existsSync(DASHBOARD_CONFIG_PATH)) {
476
+ const raw = readFileSync(DASHBOARD_CONFIG_PATH, 'utf-8');
477
+ const saved = JSON.parse(raw);
478
+ this.config = { ...this.config, ...saved };
479
+ }
480
+ }
481
+ catch { /* Silently fail */ }
482
+ }
483
+ }
484
+ //# sourceMappingURL=web-dashboard.js.map
@@ -0,0 +1,74 @@
1
+ export type HookEvent = 'SessionStart' | 'SessionEnd' | 'BeforeAgent' | 'AfterAgent' | 'BeforeModel' | 'AfterModel' | 'BeforeTool' | 'AfterTool' | 'AfterToolFailure' | 'PermissionRequest' | 'PermissionDenied' | 'UserPromptSubmit' | 'PreCompact' | 'PostCompact' | 'SubagentStart' | 'SubagentStop' | 'FileChanged' | 'CwdChanged' | 'ConfigChange';
2
+ export type HookType = 'command' | 'http' | 'prompt' | 'agent';
3
+ export interface HookDefinition {
4
+ event: HookEvent;
5
+ type: HookType;
6
+ command?: string;
7
+ url?: string;
8
+ prompt?: string;
9
+ matcher?: string;
10
+ enabled?: boolean;
11
+ }
12
+ export interface HookContext {
13
+ event: HookEvent;
14
+ toolName?: string;
15
+ toolArgs?: Record<string, unknown>;
16
+ toolResult?: string;
17
+ prompt?: string;
18
+ modelResponse?: string;
19
+ error?: string;
20
+ workingDirectory: string;
21
+ sessionId: string;
22
+ timestamp: number;
23
+ }
24
+ export interface HookResult {
25
+ action: 'continue' | 'block' | 'modify' | 'retry';
26
+ modifiedArgs?: Record<string, unknown>;
27
+ modifiedPrompt?: string;
28
+ additionalContext?: string;
29
+ reason?: string;
30
+ }
31
+ export declare class HooksSystem {
32
+ private hooks;
33
+ private globalHooks;
34
+ /**
35
+ * Register a hook
36
+ */
37
+ register(hook: HookDefinition): void;
38
+ /**
39
+ * Register a global hook that fires on all events
40
+ */
41
+ registerGlobal(hook: HookDefinition): void;
42
+ /**
43
+ * Unregister a hook
44
+ */
45
+ unregister(event: HookEvent, index: number): void;
46
+ /**
47
+ * Get all hooks for an event
48
+ */
49
+ getHooks(event: HookEvent): HookDefinition[];
50
+ /**
51
+ * Execute all hooks for an event
52
+ */
53
+ execute(event: HookEvent, context: HookContext): Promise<HookResult>;
54
+ /**
55
+ * Execute a single hook
56
+ */
57
+ private executeHook;
58
+ private executeCommandHook;
59
+ private executeHttpHook;
60
+ /**
61
+ * Load hooks from config
62
+ */
63
+ loadFromConfig(config: {
64
+ hooks?: HookDefinition[];
65
+ }): void;
66
+ /**
67
+ * List all registered hooks
68
+ */
69
+ list(): Array<{
70
+ event: HookEvent;
71
+ hooks: HookDefinition[];
72
+ }>;
73
+ }
74
+ //# sourceMappingURL=hooks.d.ts.map