@andrebuzeli/git-mcp 15.8.4 → 15.8.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.
@@ -1,198 +1,198 @@
1
- // Sistema de Métricas/Telemetria para git-mcp
2
- // Opt-in via variável de ambiente ENABLE_METRICS=true
3
-
4
- const metricsEnabled = process.env.ENABLE_METRICS === "true";
5
-
6
- // Armazena métricas em memória
7
- const metricsStore = {
8
- operations: [],
9
- errors: [],
10
- startTime: Date.now(),
11
- summary: {
12
- totalOperations: 0,
13
- successfulOperations: 0,
14
- failedOperations: 0,
15
- totalDurationMs: 0,
16
- operationsByType: {}
17
- }
18
- };
19
-
20
- /**
21
- * Registra início de uma operação
22
- * @param {string} operation - Nome da operação (ex: "git-workflow:push")
23
- * @param {Object} metadata - Metadados adicionais
24
- * @returns {string} - ID da operação para tracking
25
- */
26
- export function startOperation(operation, metadata = {}) {
27
- if (!metricsEnabled) return null;
28
-
29
- const id = `${operation}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
30
-
31
- metricsStore.operations.push({
32
- id,
33
- operation,
34
- startTime: Date.now(),
35
- endTime: null,
36
- duration: null,
37
- status: "running",
38
- metadata,
39
- error: null
40
- });
41
-
42
- return id;
43
- }
44
-
45
- /**
46
- * Finaliza uma operação
47
- * @param {string} id - ID da operação
48
- * @param {string} status - "success" ou "error"
49
- * @param {Error|null} error - Erro se houver
50
- */
51
- export function endOperation(id, status = "success", error = null) {
52
- if (!metricsEnabled || !id) return;
53
-
54
- const op = metricsStore.operations.find(o => o.id === id);
55
- if (!op) return;
56
-
57
- op.endTime = Date.now();
58
- op.duration = op.endTime - op.startTime;
59
- op.status = status;
60
-
61
- if (error) {
62
- op.error = {
63
- message: error.message || String(error),
64
- code: error.code || "UNKNOWN"
65
- };
66
- metricsStore.errors.push({
67
- timestamp: Date.now(),
68
- operation: op.operation,
69
- error: op.error
70
- });
71
- metricsStore.summary.failedOperations++;
72
- } else {
73
- metricsStore.summary.successfulOperations++;
74
- }
75
-
76
- metricsStore.summary.totalOperations++;
77
- metricsStore.summary.totalDurationMs += op.duration;
78
-
79
- // Atualiza contagem por tipo
80
- const opType = op.operation.split(":")[0];
81
- metricsStore.summary.operationsByType[opType] =
82
- (metricsStore.summary.operationsByType[opType] || 0) + 1;
83
- }
84
-
85
- /**
86
- * Wrapper para medir tempo de execução de uma função
87
- * @param {string} operation - Nome da operação
88
- * @param {Function} fn - Função async para executar
89
- * @param {Object} metadata - Metadados adicionais
90
- */
91
- export async function withMetrics(operation, fn, metadata = {}) {
92
- const id = startOperation(operation, metadata);
93
-
94
- try {
95
- const result = await fn();
96
- endOperation(id, "success");
97
- return result;
98
- } catch (error) {
99
- endOperation(id, "error", error);
100
- throw error;
101
- }
102
- }
103
-
104
- /**
105
- * Retorna resumo das métricas
106
- */
107
- export function getMetricsSummary() {
108
- if (!metricsEnabled) {
109
- return { enabled: false, message: "Métricas desabilitadas. Use ENABLE_METRICS=true para habilitar." };
110
- }
111
-
112
- const uptimeMs = Date.now() - metricsStore.startTime;
113
- const avgDuration = metricsStore.summary.totalOperations > 0
114
- ? metricsStore.summary.totalDurationMs / metricsStore.summary.totalOperations
115
- : 0;
116
-
117
- return {
118
- enabled: true,
119
- uptime: {
120
- ms: uptimeMs,
121
- formatted: formatDuration(uptimeMs)
122
- },
123
- operations: {
124
- total: metricsStore.summary.totalOperations,
125
- successful: metricsStore.summary.successfulOperations,
126
- failed: metricsStore.summary.failedOperations,
127
- successRate: metricsStore.summary.totalOperations > 0
128
- ? ((metricsStore.summary.successfulOperations / metricsStore.summary.totalOperations) * 100).toFixed(2) + "%"
129
- : "N/A"
130
- },
131
- performance: {
132
- totalDurationMs: metricsStore.summary.totalDurationMs,
133
- avgDurationMs: avgDuration.toFixed(2),
134
- operationsPerMinute: uptimeMs > 0
135
- ? ((metricsStore.summary.totalOperations / uptimeMs) * 60000).toFixed(2)
136
- : 0
137
- },
138
- byType: metricsStore.summary.operationsByType,
139
- recentErrors: metricsStore.errors.slice(-10)
140
- };
141
- }
142
-
143
- /**
144
- * Retorna operações recentes
145
- * @param {number} limit - Número máximo de operações
146
- */
147
- export function getRecentOperations(limit = 20) {
148
- if (!metricsEnabled) return [];
149
- return metricsStore.operations.slice(-limit);
150
- }
151
-
152
- /**
153
- * Limpa métricas antigas
154
- * @param {number} maxAgeMs - Idade máxima em ms (default: 1 hora)
155
- */
156
- export function pruneMetrics(maxAgeMs = 3600000) {
157
- if (!metricsEnabled) return;
158
-
159
- const cutoff = Date.now() - maxAgeMs;
160
- metricsStore.operations = metricsStore.operations.filter(o => o.startTime > cutoff);
161
- metricsStore.errors = metricsStore.errors.filter(e => e.timestamp > cutoff);
162
- }
163
-
164
- /**
165
- * Reseta todas as métricas
166
- */
167
- export function resetMetrics() {
168
- metricsStore.operations = [];
169
- metricsStore.errors = [];
170
- metricsStore.startTime = Date.now();
171
- metricsStore.summary = {
172
- totalOperations: 0,
173
- successfulOperations: 0,
174
- failedOperations: 0,
175
- totalDurationMs: 0,
176
- operationsByType: {}
177
- };
178
- }
179
-
180
- /**
181
- * Formata duração em formato legível
182
- */
183
- function formatDuration(ms) {
184
- const seconds = Math.floor(ms / 1000);
185
- const minutes = Math.floor(seconds / 60);
186
- const hours = Math.floor(minutes / 60);
187
-
188
- if (hours > 0) return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
189
- if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
190
- return `${seconds}s`;
191
- }
192
-
193
- /**
194
- * Verifica se métricas estão habilitadas
195
- */
196
- export function isMetricsEnabled() {
197
- return metricsEnabled;
198
- }
1
+ // Sistema de Métricas/Telemetria para git-mcp
2
+ // Opt-in via variável de ambiente ENABLE_METRICS=true
3
+
4
+ const metricsEnabled = process.env.ENABLE_METRICS === "true";
5
+
6
+ // Armazena métricas em memória
7
+ const metricsStore = {
8
+ operations: [],
9
+ errors: [],
10
+ startTime: Date.now(),
11
+ summary: {
12
+ totalOperations: 0,
13
+ successfulOperations: 0,
14
+ failedOperations: 0,
15
+ totalDurationMs: 0,
16
+ operationsByType: {}
17
+ }
18
+ };
19
+
20
+ /**
21
+ * Registra início de uma operação
22
+ * @param {string} operation - Nome da operação (ex: "git-workflow:push")
23
+ * @param {Object} metadata - Metadados adicionais
24
+ * @returns {string} - ID da operação para tracking
25
+ */
26
+ export function startOperation(operation, metadata = {}) {
27
+ if (!metricsEnabled) return null;
28
+
29
+ const id = `${operation}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
30
+
31
+ metricsStore.operations.push({
32
+ id,
33
+ operation,
34
+ startTime: Date.now(),
35
+ endTime: null,
36
+ duration: null,
37
+ status: "running",
38
+ metadata,
39
+ error: null
40
+ });
41
+
42
+ return id;
43
+ }
44
+
45
+ /**
46
+ * Finaliza uma operação
47
+ * @param {string} id - ID da operação
48
+ * @param {string} status - "success" ou "error"
49
+ * @param {Error|null} error - Erro se houver
50
+ */
51
+ export function endOperation(id, status = "success", error = null) {
52
+ if (!metricsEnabled || !id) return;
53
+
54
+ const op = metricsStore.operations.find(o => o.id === id);
55
+ if (!op) return;
56
+
57
+ op.endTime = Date.now();
58
+ op.duration = op.endTime - op.startTime;
59
+ op.status = status;
60
+
61
+ if (error) {
62
+ op.error = {
63
+ message: error.message || String(error),
64
+ code: error.code || "UNKNOWN"
65
+ };
66
+ metricsStore.errors.push({
67
+ timestamp: Date.now(),
68
+ operation: op.operation,
69
+ error: op.error
70
+ });
71
+ metricsStore.summary.failedOperations++;
72
+ } else {
73
+ metricsStore.summary.successfulOperations++;
74
+ }
75
+
76
+ metricsStore.summary.totalOperations++;
77
+ metricsStore.summary.totalDurationMs += op.duration;
78
+
79
+ // Atualiza contagem por tipo
80
+ const opType = op.operation.split(":")[0];
81
+ metricsStore.summary.operationsByType[opType] =
82
+ (metricsStore.summary.operationsByType[opType] || 0) + 1;
83
+ }
84
+
85
+ /**
86
+ * Wrapper para medir tempo de execução de uma função
87
+ * @param {string} operation - Nome da operação
88
+ * @param {Function} fn - Função async para executar
89
+ * @param {Object} metadata - Metadados adicionais
90
+ */
91
+ export async function withMetrics(operation, fn, metadata = {}) {
92
+ const id = startOperation(operation, metadata);
93
+
94
+ try {
95
+ const result = await fn();
96
+ endOperation(id, "success");
97
+ return result;
98
+ } catch (error) {
99
+ endOperation(id, "error", error);
100
+ throw error;
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Retorna resumo das métricas
106
+ */
107
+ export function getMetricsSummary() {
108
+ if (!metricsEnabled) {
109
+ return { enabled: false, message: "Métricas desabilitadas. Use ENABLE_METRICS=true para habilitar." };
110
+ }
111
+
112
+ const uptimeMs = Date.now() - metricsStore.startTime;
113
+ const avgDuration = metricsStore.summary.totalOperations > 0
114
+ ? metricsStore.summary.totalDurationMs / metricsStore.summary.totalOperations
115
+ : 0;
116
+
117
+ return {
118
+ enabled: true,
119
+ uptime: {
120
+ ms: uptimeMs,
121
+ formatted: formatDuration(uptimeMs)
122
+ },
123
+ operations: {
124
+ total: metricsStore.summary.totalOperations,
125
+ successful: metricsStore.summary.successfulOperations,
126
+ failed: metricsStore.summary.failedOperations,
127
+ successRate: metricsStore.summary.totalOperations > 0
128
+ ? ((metricsStore.summary.successfulOperations / metricsStore.summary.totalOperations) * 100).toFixed(2) + "%"
129
+ : "N/A"
130
+ },
131
+ performance: {
132
+ totalDurationMs: metricsStore.summary.totalDurationMs,
133
+ avgDurationMs: avgDuration.toFixed(2),
134
+ operationsPerMinute: uptimeMs > 0
135
+ ? ((metricsStore.summary.totalOperations / uptimeMs) * 60000).toFixed(2)
136
+ : 0
137
+ },
138
+ byType: metricsStore.summary.operationsByType,
139
+ recentErrors: metricsStore.errors.slice(-10)
140
+ };
141
+ }
142
+
143
+ /**
144
+ * Retorna operações recentes
145
+ * @param {number} limit - Número máximo de operações
146
+ */
147
+ export function getRecentOperations(limit = 20) {
148
+ if (!metricsEnabled) return [];
149
+ return metricsStore.operations.slice(-limit);
150
+ }
151
+
152
+ /**
153
+ * Limpa métricas antigas
154
+ * @param {number} maxAgeMs - Idade máxima em ms (default: 1 hora)
155
+ */
156
+ export function pruneMetrics(maxAgeMs = 3600000) {
157
+ if (!metricsEnabled) return;
158
+
159
+ const cutoff = Date.now() - maxAgeMs;
160
+ metricsStore.operations = metricsStore.operations.filter(o => o.startTime > cutoff);
161
+ metricsStore.errors = metricsStore.errors.filter(e => e.timestamp > cutoff);
162
+ }
163
+
164
+ /**
165
+ * Reseta todas as métricas
166
+ */
167
+ export function resetMetrics() {
168
+ metricsStore.operations = [];
169
+ metricsStore.errors = [];
170
+ metricsStore.startTime = Date.now();
171
+ metricsStore.summary = {
172
+ totalOperations: 0,
173
+ successfulOperations: 0,
174
+ failedOperations: 0,
175
+ totalDurationMs: 0,
176
+ operationsByType: {}
177
+ };
178
+ }
179
+
180
+ /**
181
+ * Formata duração em formato legível
182
+ */
183
+ function formatDuration(ms) {
184
+ const seconds = Math.floor(ms / 1000);
185
+ const minutes = Math.floor(seconds / 60);
186
+ const hours = Math.floor(minutes / 60);
187
+
188
+ if (hours > 0) return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
189
+ if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
190
+ return `${seconds}s`;
191
+ }
192
+
193
+ /**
194
+ * Verifica se métricas estão habilitadas
195
+ */
196
+ export function isMetricsEnabled() {
197
+ return metricsEnabled;
198
+ }
@@ -1,58 +1,58 @@
1
- import { withRetry } from "./retry.js";
2
-
3
- /**
4
- * Executa operações em GitHub e Gitea em PARALELO com retry automático
5
- * @param {ProviderManager} pm - Provider manager
6
- * @param {Object} handlers - { github: async (owner) => {}, gitea: async (owner) => {} }
7
- * @param {Object} options - { retry: boolean, maxRetries: number }
8
- */
9
- export async function runBoth(pm, handlers, options = {}) {
10
- const { retry = true, maxRetries = 3 } = options;
11
- const out = { github: null, gitea: null };
12
-
13
- // Buscar owners em paralelo
14
- const [ghOwner, geOwner] = await Promise.all([
15
- pm.getGitHubOwner().catch(() => ""),
16
- pm.getGiteaOwner().catch(() => "")
17
- ]);
18
-
19
- // Preparar promises para execução paralela
20
- const promises = [];
21
-
22
- if (pm.github && ghOwner && handlers.github) {
23
- const githubPromise = async () => {
24
- const executor = () => handlers.github(ghOwner);
25
- try {
26
- const result = retry ? await withRetry(executor, { maxRetries }) : await executor();
27
- return { provider: "github", result };
28
- } catch (e) {
29
- return { provider: "github", result: { ok: false, error: String(e?.message || e) } };
30
- }
31
- };
32
- promises.push(githubPromise());
33
- }
34
-
35
- if (pm.giteaUrl && pm.giteaToken && geOwner && handlers.gitea) {
36
- const giteaPromise = async () => {
37
- const executor = () => handlers.gitea(geOwner);
38
- try {
39
- const result = retry ? await withRetry(executor, { maxRetries }) : await executor();
40
- return { provider: "gitea", result };
41
- } catch (e) {
42
- return { provider: "gitea", result: { ok: false, error: String(e?.message || e) } };
43
- }
44
- };
45
- promises.push(giteaPromise());
46
- }
47
-
48
- // Executar em paralelo
49
- const results = await Promise.all(promises);
50
-
51
- // Mapear resultados
52
- for (const { provider, result } of results) {
53
- out[provider] = result;
54
- }
55
-
56
- return out;
57
- }
58
-
1
+ import { withRetry } from "./retry.js";
2
+
3
+ /**
4
+ * Executa operações em GitHub e Gitea em PARALELO com retry automático
5
+ * @param {ProviderManager} pm - Provider manager
6
+ * @param {Object} handlers - { github: async (owner) => {}, gitea: async (owner) => {} }
7
+ * @param {Object} options - { retry: boolean, maxRetries: number }
8
+ */
9
+ export async function runBoth(pm, handlers, options = {}) {
10
+ const { retry = true, maxRetries = 3 } = options;
11
+ const out = { github: null, gitea: null };
12
+
13
+ // Buscar owners em paralelo
14
+ const [ghOwner, geOwner] = await Promise.all([
15
+ pm.getGitHubOwner().catch(() => ""),
16
+ pm.getGiteaOwner().catch(() => "")
17
+ ]);
18
+
19
+ // Preparar promises para execução paralela
20
+ const promises = [];
21
+
22
+ if (pm.github && ghOwner && handlers.github) {
23
+ const githubPromise = async () => {
24
+ const executor = () => handlers.github(ghOwner);
25
+ try {
26
+ const result = retry ? await withRetry(executor, { maxRetries }) : await executor();
27
+ return { provider: "github", result };
28
+ } catch (e) {
29
+ return { provider: "github", result: { ok: false, error: String(e?.message || e) } };
30
+ }
31
+ };
32
+ promises.push(githubPromise());
33
+ }
34
+
35
+ if (pm.giteaUrl && pm.giteaToken && geOwner && handlers.gitea) {
36
+ const giteaPromise = async () => {
37
+ const executor = () => handlers.gitea(geOwner);
38
+ try {
39
+ const result = retry ? await withRetry(executor, { maxRetries }) : await executor();
40
+ return { provider: "gitea", result };
41
+ } catch (e) {
42
+ return { provider: "gitea", result: { ok: false, error: String(e?.message || e) } };
43
+ }
44
+ };
45
+ promises.push(giteaPromise());
46
+ }
47
+
48
+ // Executar em paralelo
49
+ const results = await Promise.all(promises);
50
+
51
+ // Mapear resultados
52
+ for (const { provider, result } of results) {
53
+ out[provider] = result;
54
+ }
55
+
56
+ return out;
57
+ }
58
+